linux-integrity.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/4] Trusted Key policy for TPM 2.0
@ 2021-05-21  0:43 James Bottomley
  2021-05-21  0:43 ` [PATCH 1/4] security: keys: trusted: add PCR policy to TPM2 keys James Bottomley
                   ` (4 more replies)
  0 siblings, 5 replies; 15+ messages in thread
From: James Bottomley @ 2021-05-21  0:43 UTC (permalink / raw)
  To: linux-integrity
  Cc: Mimi Zohar, Jarkko Sakkinen, David Woodhouse, keyrings, David Howells

Now that the ASN.1 representation of trusted keys is upstream we can
add policy to the keys as a sequence of policy statements meaning the
kernel can now construct and use the policy session rather than the
user having to do it and pass the session down to the kernel.  This
makes TPM 2.0 keys with policy much easier.

The format of the policy statements is compatible with the
openssl_tpm2_engine policy implementation:

https://git.kernel.org/pub/scm/linux/kernel/git/jejb/openssl_tpm2_engine.git/

And the seal_tpm2_data command in the above can be used to create
sealed keys (including with policy statements) for the kernel.

The current implementation only has a limited subset of the full TPM
2.0 policy commands, but it is enough to implement keys locked to PCR
values and expiring keys.  The main missing feature is support for the
TPM2_PolicyOR statement, which means all current policy has to be AND
chains (key doesn't unlock unless every policy statement succeeds).

I can't fix the checkpatch error in the first patch: the thing its
complaining about is a sha1 sum, just not a sha1 sum representing a
kernel commit.

James

---

James Bottomley (4):
  security: keys: trusted: add PCR policy to TPM2 keys
  security: keys: trusted: add ability to specify arbitrary policy
  security: keys: trusted: implement counter/timer policy
  security: keys: trusted: implement authorization policy

 .../security/keys/trusted-encrypted.rst       |  83 +++-
 include/keys/trusted-type.h                   |   5 +-
 include/linux/tpm.h                           |   6 +
 security/keys/Kconfig                         |   2 +
 security/keys/trusted-keys/Makefile           |   1 +
 security/keys/trusted-keys/tpm2-policy.c      | 466 ++++++++++++++++++
 security/keys/trusted-keys/tpm2-policy.h      |  31 ++
 security/keys/trusted-keys/tpm2key.asn1       |  13 +
 security/keys/trusted-keys/trusted_core.c     |   8 +-
 security/keys/trusted-keys/trusted_tpm1.c     |  15 +
 security/keys/trusted-keys/trusted_tpm2.c     | 124 ++++-
 11 files changed, 740 insertions(+), 14 deletions(-)
 create mode 100644 security/keys/trusted-keys/tpm2-policy.c
 create mode 100644 security/keys/trusted-keys/tpm2-policy.h

-- 
2.26.2


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH 1/4] security: keys: trusted: add PCR policy to TPM2 keys
  2021-05-21  0:43 [PATCH 0/4] Trusted Key policy for TPM 2.0 James Bottomley
@ 2021-05-21  0:43 ` James Bottomley
  2021-05-22 22:38   ` Jarkko Sakkinen
  2021-05-21  0:43 ` [PATCH 2/4] security: keys: trusted: add ability to specify arbitrary policy James Bottomley
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 15+ messages in thread
From: James Bottomley @ 2021-05-21  0:43 UTC (permalink / raw)
  To: linux-integrity
  Cc: Mimi Zohar, Jarkko Sakkinen, David Woodhouse, keyrings, David Howells

This commit adds the ability to specify a PCR lock policy to TPM2
keys.  There is a complexity in that the creator of the key must chose
either to use a PCR lock policy or to use authentication.  At the
current time they can't use both due to a complexity with the way
authentication works when policy registers are in use.  The way to
construct a pcrinfo statement for a key is simply to use the
TPMS_PCR_SELECT structure to specify the PCRs and follow this by a
hash of all their values in order of ascending PCR number.

For simplicity, we require the hash of the PCRs to use the same hash
algorithm as the one passed in.  Thus to construct a policy around the
value of the resettable PCR 16 using the sha1 bank, first reset the
PCR to zero giving a hash of all zeros as:

6768033e216468247bd031a0a2d9876d79818f8f

Then the TPMS_PCR_SELECT value for PCR 16 is

03000001

So create a new 32 byte key with a policy locking the key to this
value of PCR 16 with a parent key of 81000001 would be:

keyctl add trusted kmk "new 32 keyhandle=0x81000001 hash=sha1 pcrinfo=030000016768033e216468247bd031a0a2d9876d79818f8f" @u

Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
---
 .../security/keys/trusted-encrypted.rst       |  36 +-
 include/keys/trusted-type.h                   |   5 +-
 include/linux/tpm.h                           |   3 +
 security/keys/Kconfig                         |   2 +
 security/keys/trusted-keys/Makefile           |   1 +
 security/keys/trusted-keys/tpm2-policy.c      | 359 ++++++++++++++++++
 security/keys/trusted-keys/tpm2-policy.h      |  30 ++
 security/keys/trusted-keys/tpm2key.asn1       |  13 +
 security/keys/trusted-keys/trusted_core.c     |   8 +-
 security/keys/trusted-keys/trusted_tpm2.c     |  86 ++++-
 10 files changed, 530 insertions(+), 13 deletions(-)
 create mode 100644 security/keys/trusted-keys/tpm2-policy.c
 create mode 100644 security/keys/trusted-keys/tpm2-policy.h

diff --git a/Documentation/security/keys/trusted-encrypted.rst b/Documentation/security/keys/trusted-encrypted.rst
index 80d5a5af62a1..5c66f29b7a1c 100644
--- a/Documentation/security/keys/trusted-encrypted.rst
+++ b/Documentation/security/keys/trusted-encrypted.rst
@@ -156,7 +156,10 @@ Usage::
                      (40 ascii zeros)
        blobauth=     ascii hex auth for sealed data default 0x00...
                      (40 ascii zeros)
-       pcrinfo=	     ascii hex of PCR_INFO or PCR_INFO_LONG (no default)
+       pcrinfo=      ascii hex of PCR_INFO or PCR_INFO_LONG (no
+                     default) on TPM 1.2 and a TPMS_PCR_SELECTION
+                     coupled with a hash of all the selected PCRs on
+                     TPM 2.0 using the selected hash.
        pcrlock=	     pcr number to be extended to "lock" blob
        migratable=   0|1 indicating permission to reseal to new PCR values,
                      default 1 (resealing allowed)
@@ -254,6 +257,20 @@ Load a trusted key from the saved blob::
     f1f8fff03ad0acb083725535636addb08d73dedb9832da198081e5deae84bfaf0409c22b
     e4a8aea2b607ec96931e6f4d4fe563ba
 
+Create a trusted key on TPM 2.0 using an all zero value of PCR16 and
+using the NV storage root 81000001 as the parent::
+
+    $ keyctl add trusted kmk "new 32 keyhandle=0x81000001 hash=sha1 pcrinfo=030000016768033e216468247bd031a0a2d9876d79818f8f" @u
+
+Note the TPMS_PCR_SELECT value for PCR 16 is 03000001 because all
+current TPMs have 24 PCRs, so the initial 03 says there are three
+following bytes of selection and then because the bytes are big
+endian, 16 is bit zero of byte 2. the hash is the sha1 sum of all
+zeros (the value of PCR 16)::
+
+    $ dd if=/dev/zero bs=1 count=20 2>/dev/null|sha1sum
+    6768033e216468247bd031a0a2d9876d79818f8f
+
 Reseal (TPM specific) a trusted key under new PCR values::
 
     $ keyctl update 268728824 "update pcrinfo=`cat pcr.blob`"
@@ -325,11 +342,17 @@ policy::
     TPMKey ::= SEQUENCE {
         type		OBJECT IDENTIFIER
         emptyAuth	[0] EXPLICIT BOOLEAN OPTIONAL
+        policy		[1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL
         parent		INTEGER
         pubkey		OCTET STRING
         privkey		OCTET STRING
     }
 
+    TPMPolicy ::= SEQUENCE {
+	CommandCode		[0] EXPLICIT INTEGER
+	CommandPolicy		[1] EXPLICIT OCTET STRING
+    }
+
 type is what distinguishes the key even in binary form since the OID
 is provided by the TCG to be unique and thus forms a recognizable
 binary pattern at offset 3 in the key.  The OIDs currently made
@@ -355,6 +378,17 @@ is false or not present, the key requires an explicit authorization
 phrase.  This is used by most user space consumers to decide whether
 to prompt for a password.
 
+policy represents a sequence of one or more policy statements that
+must be executed successfully into a session policy register.  If
+policy isn't present then no policy is required to unlock the key, but
+if it is, commandCode is the TPM 2.0 command code of the policy
+instruction that must be executed and CommandPolicy represents the
+binary parameter area of the policy command.
+
+Note that the current sequential execution requirement means that only
+AND based policy can be constructed at the moment, so TPM2_PolicyOR is
+not currently supported.
+
 parent represents the parent key handle, either in the 0x81 MSO space,
 like 0x81000001 for the RSA primary storage key.  Userspace programmes
 also support specifying the primary handle in the 0x40 MSO space.  If
diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h
index d89fa2579ac0..749dadbd72e7 100644
--- a/include/keys/trusted-type.h
+++ b/include/keys/trusted-type.h
@@ -20,9 +20,11 @@
 #define MIN_KEY_SIZE			32
 #define MAX_KEY_SIZE			128
 #define MAX_BLOB_SIZE			512
-#define MAX_PCRINFO_SIZE		64
+#define MAX_PCRINFO_SIZE		128
 #define MAX_DIGEST_SIZE			64
 
+#define TPM2_MAX_POLICIES		16
+
 struct trusted_key_payload {
 	struct rcu_head rcu;
 	unsigned int key_len;
@@ -31,6 +33,7 @@ struct trusted_key_payload {
 	unsigned char old_format;
 	unsigned char key[MAX_KEY_SIZE + 1];
 	unsigned char blob[MAX_BLOB_SIZE];
+	struct tpm2_policies *policies;
 };
 
 struct trusted_key_options {
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index aa11fe323c56..298321fe07ee 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -230,10 +230,12 @@ enum tpm2_command_codes {
 	TPM2_CC_CONTEXT_LOAD	        = 0x0161,
 	TPM2_CC_CONTEXT_SAVE	        = 0x0162,
 	TPM2_CC_FLUSH_CONTEXT	        = 0x0165,
+	TPM2_CC_START_AUTH_SESS		= 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,
@@ -242,6 +244,7 @@ enum tpm2_command_codes {
 };
 
 enum tpm2_permanent_handles {
+	TPM2_RH_NULL		= 0x40000007,
 	TPM2_RS_PW		= 0x40000009,
 };
 
diff --git a/security/keys/Kconfig b/security/keys/Kconfig
index 64b81abd087e..8a79f4faba97 100644
--- a/security/keys/Kconfig
+++ b/security/keys/Kconfig
@@ -74,6 +74,8 @@ config TRUSTED_KEYS
 	select CRYPTO
 	select CRYPTO_HMAC
 	select CRYPTO_SHA1
+	select CRYPTO_SHA256
+	select CRYPTO_SHA512
 	select CRYPTO_HASH_INFO
 	select ASN1_ENCODER
 	select OID_REGISTRY
diff --git a/security/keys/trusted-keys/Makefile b/security/keys/trusted-keys/Makefile
index feb8b6c3cc79..39c598c4d2ef 100644
--- a/security/keys/trusted-keys/Makefile
+++ b/security/keys/trusted-keys/Makefile
@@ -10,5 +10,6 @@ trusted-y += trusted_tpm1.o
 $(obj)/trusted_tpm2.o: $(obj)/tpm2key.asn1.h
 trusted-y += trusted_tpm2.o
 trusted-y += tpm2key.asn1.o
+trusted-y += tpm2-policy.o
 
 trusted-$(CONFIG_TEE) += trusted_tee.o
diff --git a/security/keys/trusted-keys/tpm2-policy.c b/security/keys/trusted-keys/tpm2-policy.c
new file mode 100644
index 000000000000..b05b2953d5ea
--- /dev/null
+++ b/security/keys/trusted-keys/tpm2-policy.c
@@ -0,0 +1,359 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2019 James.Bottomley@HansenPartnership.com
+ */
+
+#include <linux/asn1_encoder.h>
+#include <linux/err.h>
+#include <linux/types.h>
+#include <linux/printk.h>
+#include <linux/string.h>
+#include <linux/tpm.h>
+
+#include <asm/unaligned.h>
+
+#include <crypto/hash.h>
+
+#include <keys/trusted-type.h>
+#include <keys/trusted_tpm.h>
+
+#include "tpm2key.asn1.h"
+#include "tpm2-policy.h"
+
+/* used by ASN.1 parser only to extract policy code */
+int tpm2_key_code(void *context, size_t hdrlen,
+		  unsigned char tag,
+		  const void *value, size_t vlen)
+{
+	struct tpm2_key_context *ctx = context;
+	u32 code = 0;
+	const u8 *v = value;
+	int i;
+
+	for (i = 0; i < vlen; i++) {
+		code <<= 8;
+		code |= v[i];
+	}
+
+	ctx->policy_code[ctx->policy_count] = code;
+
+	return 0;
+}
+
+/* used by ASN.1 parser only to extract policy sequence */
+int tpm2_key_policy(void *context, size_t hdrlen,
+		  unsigned char tag,
+		  const void *value, size_t vlen)
+{
+	struct tpm2_key_context *ctx = context;
+
+	ctx->policies[ctx->policy_count] = value;
+	ctx->policy_len[ctx->policy_count++] = vlen;
+
+	return 0;
+}
+
+/* we only support a limited number of policy statement so
+ * make sure we don't have anything we can't support
+ */
+static int tpm2_validate_policy(struct tpm2_policies *pols)
+{
+	int i;
+
+	if (pols->count == 0)
+		return 0;
+
+	for (i = 0; i < pols->count; i++) {
+		switch (pols->code[i]) {
+		case TPM2_CC_POLICY_PCR:
+			break;
+		default:
+			pr_warn("tpm2 policy 0x%x is unsupported",
+				pols->code[i]);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * tpm2_key_process_policy - collect the policty from the context
+ * @ctx: the context to collect from
+ * @payload: the payload structure to place it in
+ *
+ * THis function sizes the policy statements and allocates space
+ * within the payload to receive them before copying them over.  It
+ * should be used after the ber decoder has completed successfully
+ */
+int tpm2_key_policy_process(struct tpm2_key_context *ctx,
+			    struct trusted_key_payload *payload)
+{
+	int tot_len = 0;
+	u8 *buf;
+	int i, ret, len = 0;
+	struct tpm2_policies *pols;
+
+	if (ctx->policy_count == 0)
+		return 0;
+
+	for (i = 0; i < ctx->policy_count; i++)
+		tot_len += ctx->policy_len[i];
+	tot_len += sizeof(*pols);
+
+	pols = kmalloc(tot_len, GFP_KERNEL);
+	if (!pols)
+		return -ENOMEM;
+
+	payload->policies = pols;
+	buf = (u8 *)(pols + 1);
+
+	for (i = 0; i < ctx->policy_count; i++) {
+		pols->policies[i] = &buf[len];
+		pols->len[i] = ctx->policy_len[i];
+		pols->code[i] = ctx->policy_code[i];
+		if (pols->len[i])
+			memcpy(pols->policies[i], ctx->policies[i],
+			       ctx->policy_len[i]);
+		len += ctx->policy_len[i];
+	}
+	pols->count = ctx->policy_count;
+
+	ret = tpm2_validate_policy(pols);
+	if (ret) {
+		kfree(pols);
+		payload->policies = NULL;
+	}
+
+	/* capture the hash and size */
+
+	/* the hash is the second algorithm */
+	pols->hash = get_unaligned_be16(&ctx->pub[4]);
+	/* and the digest appears after the attributes */
+	pols->hash_size = get_unaligned_be16(&ctx->pub[10]);
+
+	return ret;
+}
+
+int tpm2_generate_policy_digest(struct tpm2_policies *pols,
+				u32 hash, u8 *policydigest, u32 *plen)
+{
+	int i;
+	struct crypto_shash *tfm;
+	int rc;
+
+	if (pols->count == 0)
+		return 0;
+
+	tfm = crypto_alloc_shash(hash_algo_name[hash], 0, 0);
+	if (IS_ERR(tfm))
+		return PTR_ERR(tfm);
+
+	rc = crypto_shash_digestsize(tfm);
+	if (WARN(rc > MAX_DIGEST_SIZE,
+		 "BUG: trusted key code has alg %s with digest too large (%d)",
+		 hash_algo_name[hash], rc)) {
+		rc = -EINVAL;
+		goto err;
+	}
+
+	pols->hash = hash;
+	pols->hash_size = rc;
+	*plen = rc;
+
+	/* policy digests always start out all zeros */
+	memset(policydigest, 0, rc);
+
+	for (i = 0; i < pols->count; i++) {
+		u8 *policy = pols->policies[i];
+		int len = pols->len[i];
+		u32 cmd = pols->code[i];
+		u8 code[4];
+		SHASH_DESC_ON_STACK(sdesc, tfm);
+
+		sdesc->tfm = tfm;
+		rc = crypto_shash_init(sdesc);
+		if (rc)
+			goto err;
+
+		/* first hash the previous digest */
+		crypto_shash_update(sdesc, policydigest, *plen);
+
+		/* then hash the command code */
+		put_unaligned_be32(cmd, code);
+		crypto_shash_update(sdesc, code, 4);
+
+		crypto_shash_update(sdesc, policy, len);
+
+		/* now output the intermediate to the policydigest */
+		crypto_shash_final(sdesc, policydigest);
+
+	}
+	rc = 0;
+
+ err:
+	crypto_free_shash(tfm);
+	return rc;
+}
+
+int tpm2_encode_policy(struct tpm2_policies *pols, u8 **data, u32 *len)
+{
+	const int SCRATCH_SIZE = PAGE_SIZE;
+	u8 *buf = kmalloc(2 * SCRATCH_SIZE, GFP_KERNEL);
+	u8 *work = buf + SCRATCH_SIZE;
+	u8 *ptr;
+	u8 *end_work = work + SCRATCH_SIZE;
+	int i, ret;
+
+	if (!buf)
+		return -ENOMEM;
+
+	for (i = 0; i < pols->count; i++) {
+		u8 *seq, *tag;
+		u32 cmd = pols->code[i];
+
+		if (WARN(work - buf + 14 + pols->len[i] > 2 * SCRATCH_SIZE,
+			 "BUG: scratch buffer is too small"))
+			return -EINVAL;
+
+		work = asn1_encode_sequence(work, end_work, NULL, -1);
+		seq = work;
+
+		work = asn1_encode_tag(work, end_work, 0, NULL, -1);
+		tag = work;
+
+		work = asn1_encode_integer(work, end_work, cmd);
+		asn1_encode_tag(tag, end_work, 0, NULL, work - tag);
+
+		work = asn1_encode_tag(work, end_work, 1, NULL, -1);
+		tag = work;
+
+		work = asn1_encode_octet_string(work, end_work,
+						pols->policies[i],
+						pols->len[i]);
+
+		asn1_encode_tag(tag, end_work, 1, NULL, work - tag);
+
+		seq = asn1_encode_sequence(seq, end_work, NULL, work - seq);
+		if (IS_ERR(seq)) {
+			ret = PTR_ERR(seq);
+			goto err;
+		}
+	}
+	ptr = asn1_encode_sequence(buf, buf + SCRATCH_SIZE, buf + PAGE_SIZE,
+				   work - buf - PAGE_SIZE);
+	if (IS_ERR(ptr)) {
+		ret = PTR_ERR(ptr);
+		goto err;
+	}
+
+	*data = buf;
+	*len = ptr - buf;
+
+	return 0;
+
+ err:
+	kfree(buf);
+	return ret;
+}
+
+static int tpm2_start_policy_session(struct tpm_chip *chip,
+				     u16 hash, u32 *handle)
+{
+	struct tpm_buf buf;
+	int rc;
+	int i;
+
+	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_START_AUTH_SESS);
+	if (rc)
+		return rc;
+
+	/* NULL salt key handle */
+	tpm_buf_append_u32(&buf, TPM2_RH_NULL);
+
+	/* NULL bind key handle */
+	tpm_buf_append_u32(&buf, TPM2_RH_NULL);
+
+	/* empty nonce caller */
+	tpm_buf_append_u16(&buf, 20);
+	for (i = 0; i < 20; i++)
+		tpm_buf_append_u8(&buf, 0);
+
+	/* empty auth */
+	tpm_buf_append_u16(&buf, 0);
+
+	/* session type policy */
+	tpm_buf_append_u8(&buf, 0x01);
+
+	/* symmetric encryption parameters */
+
+	/* symmetric algorithm  */
+	tpm_buf_append_u16(&buf, TPM_ALG_NULL);
+
+	/* hash algorithm for session */
+	tpm_buf_append_u16(&buf, hash);
+
+	rc = tpm_transmit_cmd(chip, &buf, 0, "start policy session");
+	if (rc)
+		goto out;
+
+	*handle = get_unaligned_be32(buf.data + TPM_HEADER_SIZE);
+
+ out:
+	tpm_buf_destroy(&buf);
+
+	return rc <= 0 ? rc : -EPERM;
+}
+
+int tpm2_get_policy_session(struct tpm_chip *chip, struct tpm2_policies *pols,
+			    u32 *handle)
+{
+	int i, rc;
+	const char *failure;
+
+	rc = tpm2_start_policy_session(chip, pols->hash, handle);
+	if (rc)
+		return rc;
+
+	for (i = 0; i < pols->count; i++) {
+		u32 cmd = pols->code[i];
+		struct tpm_buf buf;
+
+		rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, cmd);
+		if (rc)
+			return rc;
+
+		tpm_buf_append_u32(&buf, *handle);
+
+		switch (cmd) {
+		case TPM2_CC_POLICY_PCR:
+			failure = "PCR";
+			/*
+			 * for reasons best known to the TCG we have
+			 * to reverse the two arguments to send to the
+			 * policy command
+			 */
+			tpm_buf_append_u16(&buf, pols->hash_size);
+			tpm_buf_append(&buf, pols->policies[i] + pols->len[i] -
+				       pols->hash_size, pols->hash_size);
+			tpm_buf_append(&buf, pols->policies[i],
+				       pols->len[i] - pols->hash_size);
+			break;
+		default:
+			failure = "unknown policy";
+			break;
+		}
+
+		rc = tpm_transmit_cmd(chip, &buf, 0, "send policy");
+		tpm_buf_destroy(&buf);
+		if (rc) {
+			dev_err(&chip->dev, "TPM policy %s failed, rc=%d\n",
+				failure, rc);
+			tpm2_flush_context(chip, *handle);
+			*handle = 0;
+			return -EPERM;
+		}
+	}
+
+	return 0;
+}
diff --git a/security/keys/trusted-keys/tpm2-policy.h b/security/keys/trusted-keys/tpm2-policy.h
new file mode 100644
index 000000000000..46bf1f0a9325
--- /dev/null
+++ b/security/keys/trusted-keys/tpm2-policy.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+struct tpm2_key_context {
+	u32 parent;
+	const u8 *pub;
+	u32 pub_len;
+	const u8 *priv;
+	u32 priv_len;
+	const u8 *policies[TPM2_MAX_POLICIES];
+	u32 policy_code[TPM2_MAX_POLICIES];
+	u16 policy_len[TPM2_MAX_POLICIES];
+	u8 policy_count;
+};
+
+struct tpm2_policies {
+	u32 code[TPM2_MAX_POLICIES];
+	u8 *policies[TPM2_MAX_POLICIES];
+	u16 len[TPM2_MAX_POLICIES];
+	u8 count;
+	u16 hash;
+	u16 hash_size;
+};
+
+int tpm2_key_policy_process(struct tpm2_key_context *ctx,
+			    struct trusted_key_payload *payload);
+int tpm2_generate_policy_digest(struct tpm2_policies *pols, u32 hash,
+				u8 *policydigest, u32 *plen);
+int tpm2_encode_policy(struct tpm2_policies *pols, u8 **data, u32 *len);
+int tpm2_get_policy_session(struct tpm_chip *chip, struct tpm2_policies *pols,
+			    u32 *handle);
diff --git a/security/keys/trusted-keys/tpm2key.asn1 b/security/keys/trusted-keys/tpm2key.asn1
index f57f869ad600..1684bd8f725e 100644
--- a/security/keys/trusted-keys/tpm2key.asn1
+++ b/security/keys/trusted-keys/tpm2key.asn1
@@ -1,11 +1,24 @@
 ---
 --- ASN.1 for TPM 2.0 keys
 ---
+--- Note: This isn't quite the definition in the standard
+---       However, the Linux asn.1 parser doesn't understand
+---       [2] EXPLICIT SEQUENCE OF OPTIONAL
+---       So there's an extra intermediate TPMPolicySequence
+---       definition to work around this
 
 TPMKey ::= SEQUENCE {
 	type		OBJECT IDENTIFIER ({tpm2_key_type}),
 	emptyAuth	[0] EXPLICIT BOOLEAN OPTIONAL,
+	policy		[1] EXPLICIT TPMPolicySequence OPTIONAL,
 	parent		INTEGER ({tpm2_key_parent}),
 	pubkey		OCTET STRING ({tpm2_key_pub}),
 	privkey		OCTET STRING ({tpm2_key_priv})
 	}
+
+TPMPolicySequence ::= SEQUENCE OF TPMPolicy
+
+TPMPolicy ::= SEQUENCE {
+	commandCode		[0] EXPLICIT INTEGER ({tpm2_key_code}),
+	commandPolicy		[1] EXPLICIT OCTET STRING ({tpm2_key_policy})
+	}
diff --git a/security/keys/trusted-keys/trusted_core.c b/security/keys/trusted-keys/trusted_core.c
index d5c891d8d353..7a41c64e67b0 100644
--- a/security/keys/trusted-keys/trusted_core.c
+++ b/security/keys/trusted-keys/trusted_core.c
@@ -209,6 +209,7 @@ static void trusted_rcu_free(struct rcu_head *rcu)
 	struct trusted_key_payload *p;
 
 	p = container_of(rcu, struct trusted_key_payload, rcu);
+	kfree_sensitive(p->policies);
 	kfree_sensitive(p);
 }
 
@@ -299,7 +300,12 @@ static long trusted_read(const struct key *key, char *buffer,
  */
 static void trusted_destroy(struct key *key)
 {
-	kfree_sensitive(key->payload.data[0]);
+
+	struct trusted_key_payload *p;
+
+	p = key->payload.data[0];
+	kfree_sensitive(p->policies);
+	kfree_sensitive(p);
 }
 
 struct key_type key_type_trusted = {
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 0165da386289..a218f982fef5 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -17,6 +17,7 @@
 #include <asm/unaligned.h>
 
 #include "tpm2key.asn1.h"
+#include "tpm2-policy.h"
 
 static struct tpm2_hash tpm2_hash_map[] = {
 	{HASH_ALGO_SHA1, TPM_ALG_SHA1},
@@ -62,6 +63,21 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
 		work = asn1_encode_tag(work, end_work, 0, bool, w - bool);
 	}
 
+	if (payload->policies) {
+		u8 *encoded_pols;
+		u32 encoded_pol_len;
+		int ret;
+
+		ret = tpm2_encode_policy(payload->policies, &encoded_pols,
+					 &encoded_pol_len);
+		if (ret)
+			return ret;
+
+		work = asn1_encode_tag(work, end_work, 1, encoded_pols,
+				       encoded_pol_len);
+		kfree(encoded_pols);
+	}
+
 	/*
 	 * Assume both octet strings will encode to a 2 byte definite length
 	 *
@@ -85,14 +101,6 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
 	return work1 - payload->blob;
 }
 
-struct tpm2_key_context {
-	u32 parent;
-	const u8 *pub;
-	u32 pub_len;
-	const u8 *priv;
-	u32 priv_len;
-};
-
 static int tpm2_key_decode(struct trusted_key_payload *payload,
 			   struct trusted_key_options *options,
 			   u8 **buf)
@@ -115,6 +123,12 @@ static int tpm2_key_decode(struct trusted_key_payload *payload,
 	if (!blob)
 		return -ENOMEM;
 
+	ret = tpm2_key_policy_process(&ctx, payload);
+	if (ret) {
+		kfree(blob);
+		return ret;
+	}
+
 	*buf = blob;
 	options->keyhandle = ctx.parent;
 
@@ -248,6 +262,42 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 	if (!options->keyhandle)
 		return -EINVAL;
 
+	if (options->pcrinfo_len != 0) {
+		struct tpm2_policies *pols;
+		static u8 *scratch;
+		/* 4 array len, 2 hash alg */
+		const int len = 4 + 2 + options->pcrinfo_len;
+
+		pols = kmalloc(sizeof(*pols) + len, GFP_KERNEL);
+		if (!pols)
+			return -ENOMEM;
+
+		pols->count = 1;
+		pols->len[0] = len;
+		scratch = (u8 *)(pols + 1);
+		pols->policies[0] = scratch;
+		pols->code[0] = TPM2_CC_POLICY_PCR;
+
+		put_unaligned_be32(1, &scratch[0]);
+		put_unaligned_be16(hash, &scratch[4]);
+		memcpy(&scratch[6], options->pcrinfo, options->pcrinfo_len);
+		payload->policies = pols;
+	}
+
+	if (options->policydigest_len != 0 && payload->policies) {
+		/* can't specify both a digest and a policy */
+		return -EINVAL;
+	}
+
+	if (payload->policies) {
+		rc = tpm2_generate_policy_digest(payload->policies,
+						 options->hash,
+						 options->policydigest,
+						 &options->policydigest_len);
+		if (rc)
+			return rc;
+	}
+
 	rc = tpm_try_get_ops(chip);
 	if (rc)
 		return rc;
@@ -464,21 +514,37 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
 	u16 data_len;
 	u8 *data;
 	int rc;
+	u32 policyhandle;
+
+	if (payload->policies && options->policyhandle)
+		/* can't have both a passed in policy and a key resident one */
+		return -EINVAL;
 
 	rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
 	if (rc)
 		return rc;
 
+	if (payload->policies) {
+		rc = tpm2_get_policy_session(chip, payload->policies,
+					     &policyhandle);
+		if (rc)
+			return rc;
+	} else {
+		policyhandle = options->policyhandle;
+	}
+
 	tpm_buf_append_u32(&buf, blob_handle);
 	tpm2_buf_append_auth(&buf,
-			     options->policyhandle ?
-			     options->policyhandle : TPM2_RS_PW,
+			     policyhandle ?
+			     policyhandle : TPM2_RS_PW,
 			     NULL /* nonce */, 0,
 			     TPM2_SA_CONTINUE_SESSION,
 			     options->blobauth /* hmac */,
 			     options->blobauth_len);
 
 	rc = tpm_transmit_cmd(chip, &buf, 6, "unsealing");
+	if (payload->policies)
+		tpm2_flush_context(chip, policyhandle);
 	if (rc > 0)
 		rc = -EPERM;
 
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 2/4] security: keys: trusted: add ability to specify arbitrary policy
  2021-05-21  0:43 [PATCH 0/4] Trusted Key policy for TPM 2.0 James Bottomley
  2021-05-21  0:43 ` [PATCH 1/4] security: keys: trusted: add PCR policy to TPM2 keys James Bottomley
@ 2021-05-21  0:43 ` James Bottomley
  2021-05-22 22:40   ` Jarkko Sakkinen
  2021-05-21  0:44 ` [PATCH 3/4] security: keys: trusted: implement counter/timer policy James Bottomley
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 15+ messages in thread
From: James Bottomley @ 2021-05-21  0:43 UTC (permalink / raw)
  To: linux-integrity
  Cc: Mimi Zohar, Jarkko Sakkinen, David Woodhouse, keyrings, David Howells

This patch adds a policy= argument to key creation.  The policy is the
standard tss policymaker format and each separate policy line must
have a newline after it.

Thus to construct a policy requiring authorized value and pcr 16
locking using a sha256 hash, the policy (policy.txt) file would be two
lines:

0000017F00000001000B03000001303095B49BE85E381E5B20E557E46363EF55B0F43B132C2D8E3DE9AC436656F2
0000016b

This can be inserted into the key with

keyctl add trusted kmk "new 32 policy=`cat policy.txt` keyhandle=0x81000001 hash=sha256" @u

Note that although a few policies work like this, most require special
handling which must be added to the kernel policy construction
routine.

Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
---
 .../security/keys/trusted-encrypted.rst       | 16 ++++++
 security/keys/trusted-keys/tpm2-policy.c      | 53 +++++++++++++++++++
 security/keys/trusted-keys/tpm2-policy.h      |  1 +
 security/keys/trusted-keys/trusted_tpm1.c     | 15 ++++++
 security/keys/trusted-keys/trusted_tpm2.c     |  4 ++
 5 files changed, 89 insertions(+)

diff --git a/Documentation/security/keys/trusted-encrypted.rst b/Documentation/security/keys/trusted-encrypted.rst
index 5c66f29b7a1c..883844c95e91 100644
--- a/Documentation/security/keys/trusted-encrypted.rst
+++ b/Documentation/security/keys/trusted-encrypted.rst
@@ -172,6 +172,9 @@ 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.
+       policy=       specify an arbitrary set of policies.  These must
+                     be in policymaker format with each separate
+                     policy line newline terminated.
 
 "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.
@@ -271,6 +274,19 @@ zeros (the value of PCR 16)::
     $ dd if=/dev/zero bs=1 count=20 2>/dev/null|sha1sum
     6768033e216468247bd031a0a2d9876d79818f8f
 
+You can also specify arbitrary policy in policymaker format, so a two
+value policy (the pcr example above and authvalue) would look like
+this in policymaker format::
+
+    0000017F000000010004030000016768033e216468247bd031a0a2d9876d79818f8f
+    0000016b
+
+This can be placed in a file (say policy.txt) and then added to the key as::
+
+    $ keyctl add trusted kmk "new 32 keyhandle=0x81000001 hash=sha1 policy=`cat policy.txt`" @u
+
+The newlines in the file policy.txt will be automatically processed.
+
 Reseal (TPM specific) a trusted key under new PCR values::
 
     $ keyctl update 268728824 "update pcrinfo=`cat pcr.blob`"
diff --git a/security/keys/trusted-keys/tpm2-policy.c b/security/keys/trusted-keys/tpm2-policy.c
index b05b2953d5ea..bb28c864fa9b 100644
--- a/security/keys/trusted-keys/tpm2-policy.c
+++ b/security/keys/trusted-keys/tpm2-policy.c
@@ -357,3 +357,56 @@ int tpm2_get_policy_session(struct tpm_chip *chip, struct tpm2_policies *pols,
 
 	return 0;
 }
+
+int tpm2_parse_policies(struct tpm2_policies **ppols, char *str)
+{
+	struct tpm2_policies *pols;
+	char *p;
+	u8 *ptr;
+	int i = 0, left = PAGE_SIZE, res;
+
+	pols = kmalloc(left, GFP_KERNEL);
+	if (!pols)
+		return -ENOMEM;
+
+	ptr = (u8 *)(pols + 1);
+	left -= ptr - (u8 *)pols;
+
+	while ((p = strsep(&str, "\n"))) {
+		if (*p == '\0' || *p == '\n')
+			continue;
+
+		pols->len[i] = strlen(p)/2;
+		if (pols->len[i] > left) {
+			res = -E2BIG;
+			goto err;
+		}
+
+		res = hex2bin(ptr, p, pols->len[i]);
+		if (res)
+			goto err;
+
+		/* get command code and skip past */
+		pols->code[i] = get_unaligned_be32(ptr);
+		pols->policies[i] = ptr + 4;
+		ptr += pols->len[i];
+		left -= pols->len[i];
+		pols->len[i] -= 4;
+
+		/*
+		 * FIXME: this does leave the code embedded in dead
+		 * regions of the memory, but it's easier than
+		 * hexdumping to a temporary or copying over
+		 */
+		i++;
+	}
+
+	pols->count = i;
+	*ppols = pols;
+
+	return 0;
+
+ err:
+	kfree(pols);
+	return res;
+}
diff --git a/security/keys/trusted-keys/tpm2-policy.h b/security/keys/trusted-keys/tpm2-policy.h
index 46bf1f0a9325..0da013116c1c 100644
--- a/security/keys/trusted-keys/tpm2-policy.h
+++ b/security/keys/trusted-keys/tpm2-policy.h
@@ -28,3 +28,4 @@ int tpm2_generate_policy_digest(struct tpm2_policies *pols, u32 hash,
 int tpm2_encode_policy(struct tpm2_policies *pols, u8 **data, u32 *len);
 int tpm2_get_policy_session(struct tpm_chip *chip, struct tpm2_policies *pols,
 			    u32 *handle);
+int tpm2_parse_policies(struct tpm2_policies **ppols, char *str);
diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
index aa108bea6739..6ed7303e36b5 100644
--- a/security/keys/trusted-keys/trusted_tpm1.c
+++ b/security/keys/trusted-keys/trusted_tpm1.c
@@ -22,6 +22,8 @@
 
 #include <keys/trusted_tpm.h>
 
+#include "tpm2-policy.h"
+
 static const char hmac_alg[] = "hmac(sha1)";
 static const char hash_alg[] = "sha1";
 static struct tpm_chip *chip;
@@ -713,6 +715,7 @@ enum {
 	Opt_hash,
 	Opt_policydigest,
 	Opt_policyhandle,
+	Opt_policy,
 };
 
 static const match_table_t key_tokens = {
@@ -725,6 +728,7 @@ static const match_table_t key_tokens = {
 	{Opt_hash, "hash=%s"},
 	{Opt_policydigest, "policydigest=%s"},
 	{Opt_policyhandle, "policyhandle=%s"},
+	{Opt_policy, "policy=%s"},
 	{Opt_err, NULL}
 };
 
@@ -858,6 +862,17 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
 				return -EINVAL;
 			opt->policyhandle = handle;
 			break;
+
+		case Opt_policy:
+			if (pay->policies)
+				return -EINVAL;
+			if (!tpm2)
+				return -EINVAL;
+			res = tpm2_parse_policies(&pay->policies, args[0].from);
+			if (res)
+				return res;
+			break;
+
 		default:
 			return -EINVAL;
 		}
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index a218f982fef5..afe9cc41885e 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -268,6 +268,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		/* 4 array len, 2 hash alg */
 		const int len = 4 + 2 + options->pcrinfo_len;
 
+		if (payload->policies)
+			/* can't specify pcr and general policy */
+			return -EINVAL;
+
 		pols = kmalloc(sizeof(*pols) + len, GFP_KERNEL);
 		if (!pols)
 			return -ENOMEM;
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 3/4] security: keys: trusted: implement counter/timer policy
  2021-05-21  0:43 [PATCH 0/4] Trusted Key policy for TPM 2.0 James Bottomley
  2021-05-21  0:43 ` [PATCH 1/4] security: keys: trusted: add PCR policy to TPM2 keys James Bottomley
  2021-05-21  0:43 ` [PATCH 2/4] security: keys: trusted: add ability to specify arbitrary policy James Bottomley
@ 2021-05-21  0:44 ` James Bottomley
  2021-05-21  0:44 ` [PATCH 4/4] security: keys: trusted: implement authorization policy James Bottomley
  2021-05-21 13:48 ` [PATCH 0/4] Trusted Key policy for TPM 2.0 David Woodhouse
  4 siblings, 0 replies; 15+ messages in thread
From: James Bottomley @ 2021-05-21  0:44 UTC (permalink / raw)
  To: linux-integrity
  Cc: Mimi Zohar, Jarkko Sakkinen, David Woodhouse, keyrings, David Howells

This is actually a generic policy allowing a range of comparisons
against any value set in the TPM Clock, which includes things like the
reset count, a monotonic millisecond count and the restart count.  The
most useful comparison is against the millisecond count for expiring
keys.  However, you have to remember that currently Linux doesn't try
to sync the epoch timer with the TPM, so the expiration is actually
measured in how long the TPM itself has been powered on ... the TPM
timer doesn't count while the system is powered down.  The millisecond
counter is a u64 quantity found at offset 8 in the timer structure,
and the <= comparision operand is 9, so a policy set to expire after the
TPM has been up for 100 seconds would look like

0000016d00000000000f424000080009

Where 0x16d is the counter timer policy code and 0xf4240 is 100 000 in
hex.

Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
---
 .../security/keys/trusted-encrypted.rst       | 31 +++++++++++++-
 include/linux/tpm.h                           |  1 +
 security/keys/trusted-keys/tpm2-policy.c      | 42 +++++++++++++++++++
 security/keys/trusted-keys/trusted_tpm2.c     |  3 +-
 4 files changed, 75 insertions(+), 2 deletions(-)

diff --git a/Documentation/security/keys/trusted-encrypted.rst b/Documentation/security/keys/trusted-encrypted.rst
index 883844c95e91..c40e38200c1e 100644
--- a/Documentation/security/keys/trusted-encrypted.rst
+++ b/Documentation/security/keys/trusted-encrypted.rst
@@ -346,7 +346,6 @@ Another new format 'enc32' has been defined in order to support encrypted keys
 with payload size of 32 bytes. This will initially be used for nvdimm security
 but may expand to other usages that require 32 bytes payload.
 
-
 TPM 2.0 ASN.1 Key Format
 ------------------------
 
@@ -420,3 +419,33 @@ string length.
 privkey is the binary representation of TPM2B_PUBLIC excluding the
 initial TPM2B header which can be reconstructed from the ASN.1 octed
 string length.
+
+
+Appendix
+--------
+
+TPM 2.0 Policies
+----------------
+
+The current TPM supports PCR lock policies as documented above and
+CounterTimer policies which can be used to create expiring keys.  One
+caveat with expiring keys is that the TPM millisecond counter does not
+update while a system is powered off and Linux does not sync the TPM
+millisecond count with its internal clock, so the best you can expire
+in is in terms of how long any given TPM has been powered on.  (FIXME:
+Linux should simply update the millisecond clock to the current number
+of seconds past the epoch on boot).
+
+A CounterTimer policy is expressed in terms of length and offset
+against the TPM clock structure (TPMS_TIME_INFO), which looks like the
+packed structure::
+
+    struct tpms_time_info {
+            u64 uptime;       /* time in ms since last start or reset */
+	    u64 clock;        /* cumulative uptime in ms */
+	    u32 resetcount;   /* numer of times the TPM has been reset */
+	    u32 restartcount; /* number of times the TPM has been restarted */
+	    u8  safe          /* time was safely loaded from NVRam */
+    };
+
+The usual comparison for expiring keys is against clock, at offset 8.
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 298321fe07ee..85e167ae2c65 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -230,6 +230,7 @@ enum tpm2_command_codes {
 	TPM2_CC_CONTEXT_LOAD	        = 0x0161,
 	TPM2_CC_CONTEXT_SAVE	        = 0x0162,
 	TPM2_CC_FLUSH_CONTEXT	        = 0x0165,
+	TPM2_CC_POLICY_COUNTER_TIMER	= 0x016D,
 	TPM2_CC_START_AUTH_SESS		= 0x0176,
 	TPM2_CC_VERIFY_SIGNATURE        = 0x0177,
 	TPM2_CC_GET_CAPABILITY	        = 0x017A,
diff --git a/security/keys/trusted-keys/tpm2-policy.c b/security/keys/trusted-keys/tpm2-policy.c
index bb28c864fa9b..21bfeb686287 100644
--- a/security/keys/trusted-keys/tpm2-policy.c
+++ b/security/keys/trusted-keys/tpm2-policy.c
@@ -65,6 +65,7 @@ static int tpm2_validate_policy(struct tpm2_policies *pols)
 
 	for (i = 0; i < pols->count; i++) {
 		switch (pols->code[i]) {
+		case TPM2_CC_POLICY_COUNTER_TIMER:
 		case TPM2_CC_POLICY_PCR:
 			break;
 		default:
@@ -168,6 +169,7 @@ int tpm2_generate_policy_digest(struct tpm2_policies *pols,
 		u8 *policy = pols->policies[i];
 		int len = pols->len[i];
 		u32 cmd = pols->code[i];
+		u8 digest[MAX_DIGEST_SIZE];
 		u8 code[4];
 		SHASH_DESC_ON_STACK(sdesc, tfm);
 
@@ -183,6 +185,19 @@ int tpm2_generate_policy_digest(struct tpm2_policies *pols,
 		put_unaligned_be32(cmd, code);
 		crypto_shash_update(sdesc, code, 4);
 
+		/* commands that need special handling */
+		if (cmd == TPM2_CC_POLICY_COUNTER_TIMER) {
+			SHASH_DESC_ON_STACK(sdesc1, tfm);
+
+			sdesc1->tfm = tfm;
+
+			/* counter timer policies are double hashed */
+			crypto_shash_digest(sdesc1, policy, len,
+					    digest);
+			policy = digest;
+			len = *plen;
+		}
+
 		crypto_shash_update(sdesc, policy, len);
 
 		/* now output the intermediate to the policydigest */
@@ -339,8 +354,35 @@ int tpm2_get_policy_session(struct tpm_chip *chip, struct tpm2_policies *pols,
 			tpm_buf_append(&buf, pols->policies[i],
 				       pols->len[i] - pols->hash_size);
 			break;
+
+		case TPM2_CC_POLICY_COUNTER_TIMER: {
+			/*
+			 * the format of this is the last two u16
+			 * quantities are the offset and operation
+			 * respectively.  The rest is operandB which
+			 * must be zero padded in a hash digest
+			 */
+			u16 opb_len = pols->len[i] - 4;
+
+			if (opb_len > pols->hash_size)
+				return -EINVAL;
+
+			tpm_buf_append_u16(&buf, opb_len);
+			tpm_buf_append(&buf, pols->policies[i], opb_len);
+
+			/* offset and operand*/
+			tpm_buf_append(&buf, pols->policies[i] + opb_len, 4);
+			failure = "Counter Timer";
+
+			break;
+		}
+
 		default:
 			failure = "unknown policy";
+			if (pols->len[i])
+				tpm_buf_append(&buf, pols->policies[i],
+					       pols->len[i]);
+
 			break;
 		}
 
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index afe9cc41885e..b7eb919de8ef 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -248,6 +248,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 	u32 flags;
 	int i;
 	int rc;
+	static const int POLICY_SIZE = 2 * PAGE_SIZE;
 
 	for (i = 0; i < ARRAY_SIZE(tpm2_hash_map); i++) {
 		if (options->hash == tpm2_hash_map[i].crypto_id) {
@@ -272,7 +273,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 			/* can't specify pcr and general policy */
 			return -EINVAL;
 
-		pols = kmalloc(sizeof(*pols) + len, GFP_KERNEL);
+		pols = kmalloc(POLICY_SIZE, GFP_KERNEL);
 		if (!pols)
 			return -ENOMEM;
 
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 4/4] security: keys: trusted: implement authorization policy
  2021-05-21  0:43 [PATCH 0/4] Trusted Key policy for TPM 2.0 James Bottomley
                   ` (2 preceding siblings ...)
  2021-05-21  0:44 ` [PATCH 3/4] security: keys: trusted: implement counter/timer policy James Bottomley
@ 2021-05-21  0:44 ` James Bottomley
  2021-05-21 13:48 ` [PATCH 0/4] Trusted Key policy for TPM 2.0 David Woodhouse
  4 siblings, 0 replies; 15+ messages in thread
From: James Bottomley @ 2021-05-21  0:44 UTC (permalink / raw)
  To: linux-integrity
  Cc: Mimi Zohar, Jarkko Sakkinen, David Woodhouse, keyrings, David Howells

When using authorizations (passwords) with a policy, the trigger for
the authorizations must be present in the policy statements that are
required to build to the policy hash.  Add this required policy statement
if blobauth is present.

Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
---
 include/linux/tpm.h                       |  2 ++
 security/keys/trusted-keys/tpm2-policy.c  | 14 +++++++++-
 security/keys/trusted-keys/trusted_tpm2.c | 33 +++++++++++++++++++++++
 3 files changed, 48 insertions(+), 1 deletion(-)

diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 85e167ae2c65..081ed7d6d4ac 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -230,6 +230,7 @@ enum tpm2_command_codes {
 	TPM2_CC_CONTEXT_LOAD	        = 0x0161,
 	TPM2_CC_CONTEXT_SAVE	        = 0x0162,
 	TPM2_CC_FLUSH_CONTEXT	        = 0x0165,
+	TPM2_CC_POLICY_AUTHVALUE	= 0x016B,
 	TPM2_CC_POLICY_COUNTER_TIMER	= 0x016D,
 	TPM2_CC_START_AUTH_SESS		= 0x0176,
 	TPM2_CC_VERIFY_SIGNATURE        = 0x0177,
@@ -240,6 +241,7 @@ enum tpm2_command_codes {
 	TPM2_CC_PCR_EXTEND	        = 0x0182,
 	TPM2_CC_EVENT_SEQUENCE_COMPLETE = 0x0185,
 	TPM2_CC_HASH_SEQUENCE_START     = 0x0186,
+	TPM2_CC_POLICY_PASSWORD		= 0x018c,
 	TPM2_CC_CREATE_LOADED           = 0x0191,
 	TPM2_CC_LAST		        = 0x0193, /* Spec 1.36 */
 };
diff --git a/security/keys/trusted-keys/tpm2-policy.c b/security/keys/trusted-keys/tpm2-policy.c
index 21bfeb686287..0f5daa5653bf 100644
--- a/security/keys/trusted-keys/tpm2-policy.c
+++ b/security/keys/trusted-keys/tpm2-policy.c
@@ -67,6 +67,7 @@ static int tpm2_validate_policy(struct tpm2_policies *pols)
 		switch (pols->code[i]) {
 		case TPM2_CC_POLICY_COUNTER_TIMER:
 		case TPM2_CC_POLICY_PCR:
+		case TPM2_CC_POLICY_AUTHVALUE:
 			break;
 		default:
 			pr_warn("tpm2 policy 0x%x is unsupported",
@@ -198,7 +199,8 @@ int tpm2_generate_policy_digest(struct tpm2_policies *pols,
 			len = *plen;
 		}
 
-		crypto_shash_update(sdesc, policy, len);
+		if (len)
+			crypto_shash_update(sdesc, policy, len);
 
 		/* now output the intermediate to the policydigest */
 		crypto_shash_final(sdesc, policydigest);
@@ -334,6 +336,16 @@ int tpm2_get_policy_session(struct tpm_chip *chip, struct tpm2_policies *pols,
 		u32 cmd = pols->code[i];
 		struct tpm_buf buf;
 
+		if (cmd == TPM2_CC_POLICY_AUTHVALUE)
+			/*
+			 * both PolicyAuthValue and PolicyPassword
+			 * hash to the same thing, but one triggers
+			 * HMAC authentication and the other simple
+			 * authentication.  Since we have no HMAC
+			 * code, we're choosing the simple
+			 */
+			cmd = TPM2_CC_POLICY_PASSWORD;
+
 		rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, cmd);
 		if (rc)
 			return rc;
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index b7eb919de8ef..282c633bd2ec 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -294,6 +294,39 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		return -EINVAL;
 	}
 
+	/*
+	 * if we already have a policy, we have to add authorization
+	 * to it.  If we don't, we can simply follow the usual
+	 * non-policy route.
+	 */
+	if (options->blobauth_len != 0 && payload->policies) {
+		struct tpm2_policies *pols;
+		static u8 *scratch;
+		int i;
+		bool found = false;
+
+		pols = payload->policies;
+
+		/* make sure it's not already in policy */
+		for (i = 0; i < pols->count; i++) {
+			if (pols->code[i] == TPM2_CC_POLICY_AUTHVALUE) {
+				found = true;
+
+				break;
+			}
+		}
+
+		if (!found) {
+			i = pols->count++;
+			scratch = pols->policies[i - 1] + pols->len[i - 1];
+
+			/* the TPM2_PolicyPassword command has no payload */
+			pols->policies[i] = scratch;
+			pols->len[i] = 0;
+			pols->code[i] = TPM2_CC_POLICY_AUTHVALUE;
+		}
+	}
+
 	if (payload->policies) {
 		rc = tpm2_generate_policy_digest(payload->policies,
 						 options->hash,
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH 0/4] Trusted Key policy for TPM 2.0
  2021-05-21  0:43 [PATCH 0/4] Trusted Key policy for TPM 2.0 James Bottomley
                   ` (3 preceding siblings ...)
  2021-05-21  0:44 ` [PATCH 4/4] security: keys: trusted: implement authorization policy James Bottomley
@ 2021-05-21 13:48 ` David Woodhouse
  2021-05-21 14:28   ` James Bottomley
  2021-05-22 22:45   ` Jarkko Sakkinen
  4 siblings, 2 replies; 15+ messages in thread
From: David Woodhouse @ 2021-05-21 13:48 UTC (permalink / raw)
  To: James Bottomley, linux-integrity
  Cc: Mimi Zohar, Jarkko Sakkinen, keyrings, David Howells

[-- Attachment #1: Type: text/plain, Size: 848 bytes --]

On Thu, 2021-05-20 at 17:43 -0700, James Bottomley wrote:
> Now that the ASN.1 representation of trusted keys is upstream we can
> add policy to the keys as a sequence of policy statements meaning the
> kernel can now construct and use the policy session rather than the
> user having to do it and pass the session down to the kernel.  This
> makes TPM 2.0 keys with policy much easier.
> 
> The format of the policy statements is compatible with the
> openssl_tpm2_engine policy implementation:
> 
> https://git.kernel.org/pub/scm/linux/kernel/git/jejb/openssl_tpm2_engine.git/
> 
> And the seal_tpm2_data command in the above can be used to create
> sealed keys (including with policy statements) for the kernel.

I'd love to see that format properly defined and documented instead of
just a reference to another implementation.

[-- Attachment #2: smime.p7s --]
[-- Type: application/x-pkcs7-signature, Size: 5174 bytes --]

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 0/4] Trusted Key policy for TPM 2.0
  2021-05-21 13:48 ` [PATCH 0/4] Trusted Key policy for TPM 2.0 David Woodhouse
@ 2021-05-21 14:28   ` James Bottomley
  2021-05-21 15:22     ` David Woodhouse
  2021-05-22 22:45   ` Jarkko Sakkinen
  1 sibling, 1 reply; 15+ messages in thread
From: James Bottomley @ 2021-05-21 14:28 UTC (permalink / raw)
  To: David Woodhouse, linux-integrity
  Cc: Mimi Zohar, Jarkko Sakkinen, keyrings, David Howells

On Fri, 2021-05-21 at 14:48 +0100, David Woodhouse wrote:
> On Thu, 2021-05-20 at 17:43 -0700, James Bottomley wrote:
> > Now that the ASN.1 representation of trusted keys is upstream we
> > can add policy to the keys as a sequence of policy statements
> > meaning the kernel can now construct and use the policy session
> > rather than the user having to do it and pass the session down to
> > the kernel.  This makes TPM 2.0 keys with policy much easier.
> > 
> > The format of the policy statements is compatible with the
> > openssl_tpm2_engine policy implementation:
> > 
> > https://git.kernel.org/pub/scm/linux/kernel/git/jejb/openssl_tpm2_engine.git/
> > 
> > And the seal_tpm2_data command in the above can be used to create
> > sealed keys (including with policy statements) for the kernel.
> 
> I'd love to see that format properly defined and documented instead
> of just a reference to another implementation.

Well if you want to help me write an RFC, I can try to submit it.

James



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 0/4] Trusted Key policy for TPM 2.0
  2021-05-21 14:28   ` James Bottomley
@ 2021-05-21 15:22     ` David Woodhouse
  2021-05-21 15:55       ` James Bottomley
  0 siblings, 1 reply; 15+ messages in thread
From: David Woodhouse @ 2021-05-21 15:22 UTC (permalink / raw)
  To: James Bottomley, linux-integrity
  Cc: Mimi Zohar, Jarkko Sakkinen, keyrings, David Howells

[-- Attachment #1: Type: text/plain, Size: 1525 bytes --]

On Fri, 2021-05-21 at 07:28 -0700, James Bottomley wrote:
> On Fri, 2021-05-21 at 14:48 +0100, David Woodhouse wrote:
> > On Thu, 2021-05-20 at 17:43 -0700, James Bottomley wrote:
> > > Now that the ASN.1 representation of trusted keys is upstream we
> > > can add policy to the keys as a sequence of policy statements
> > > meaning the kernel can now construct and use the policy session
> > > rather than the user having to do it and pass the session down to
> > > the kernel.  This makes TPM 2.0 keys with policy much easier.
> > > 
> > > The format of the policy statements is compatible with the
> > > openssl_tpm2_engine policy implementation:
> > > 
> > > https://git.kernel.org/pub/scm/linux/kernel/git/jejb/openssl_tpm2_engine.git/
> > > 
> > > And the seal_tpm2_data command in the above can be used to create
> > > sealed keys (including with policy statements) for the kernel.
> > 
> > I'd love to see that format properly defined and documented instead
> > of just a reference to another implementation.
> 
> Well if you want to help me write an RFC, I can try to submit it.

The xml2rfc tool makes it fairly easy.

See https://github.com/dwmw2/ietf-cert-best-practice for a template; in
Appendix B there is an example of specifying an ASN.1 format.

We should probably define not just the ASN.1 format but also a URI
scheme for referencing objects in NVRAM. A TPMv2 version of 
https://datatracker.ietf.org/doc/html/draft-mavrogiannopoulos-tpmuri-01
might be a good idea.





[-- Attachment #2: smime.p7s --]
[-- Type: application/x-pkcs7-signature, Size: 5174 bytes --]

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 0/4] Trusted Key policy for TPM 2.0
  2021-05-21 15:22     ` David Woodhouse
@ 2021-05-21 15:55       ` James Bottomley
  2021-05-21 16:12         ` David Woodhouse
  0 siblings, 1 reply; 15+ messages in thread
From: James Bottomley @ 2021-05-21 15:55 UTC (permalink / raw)
  To: David Woodhouse, linux-integrity
  Cc: Mimi Zohar, Jarkko Sakkinen, keyrings, David Howells

On Fri, 2021-05-21 at 16:22 +0100, David Woodhouse wrote:
> On Fri, 2021-05-21 at 07:28 -0700, James Bottomley wrote:
> > On Fri, 2021-05-21 at 14:48 +0100, David Woodhouse wrote:
> > > On Thu, 2021-05-20 at 17:43 -0700, James Bottomley wrote:
> > > > Now that the ASN.1 representation of trusted keys is upstream
> > > > we can add policy to the keys as a sequence of policy
> > > > statements meaning the kernel can now construct and use the
> > > > policy session rather than the user having to do it and pass
> > > > the session down to the kernel.  This makes TPM 2.0 keys with
> > > > policy much easier.
> > > > 
> > > > The format of the policy statements is compatible with the
> > > > openssl_tpm2_engine policy implementation:
> > > > 
> > > > https://git.kernel.org/pub/scm/linux/kernel/git/jejb/openssl_tpm2_engine.git/
> > > > 
> > > > And the seal_tpm2_data command in the above can be used to
> > > > create sealed keys (including with policy statements) for the
> > > > kernel.
> > > 
> > > I'd love to see that format properly defined and documented
> > > instead of just a reference to another implementation.
> > 
> > Well if you want to help me write an RFC, I can try to submit it.
> 
> The xml2rfc tool makes it fairly easy.

XML and easy ... there's two words you don't often see in a sentence
...

> See https://github.com/dwmw2/ietf-cert-best-practice for a template;
> in Appendix B there is an example of specifying an ASN.1 format.

OK, I'll add it to 

https://git.kernel.org/pub/scm/linux/kernel/git/jejb/openssl_tpm2_engine.git/

But I'll be expecting patches ...

> We should probably define not just the ASN.1 format but also a URI
> scheme for referencing objects in NVRAM. A TPMv2 version of 
> https://datatracker.ietf.org/doc/html/draft-mavrogiannopoulos-tpmuri-01
> might be a good idea.

I'm not so sure ... the keys are files not tokens and the pkcs11 URI
doesn't have a file pointer.  I suspect the correct way to represent
them would be to fully represent the key in the URI, which sounds like
a length explosion.

If you really want to use PKCS11 for TPM keys, I've actually got an
engine exporter for any openssl engine key:

https://git.kernel.org/pub/scm/linux/kernel/git/jejb/openssl-pkcs11-export.git/

So you can refer to them with standard PKCS11 URI components instead of
making your own up.  I use it for TPM keys in firefox for instance.

James



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 0/4] Trusted Key policy for TPM 2.0
  2021-05-21 15:55       ` James Bottomley
@ 2021-05-21 16:12         ` David Woodhouse
  2021-05-21 16:17           ` James Bottomley
  0 siblings, 1 reply; 15+ messages in thread
From: David Woodhouse @ 2021-05-21 16:12 UTC (permalink / raw)
  To: James Bottomley, linux-integrity
  Cc: Mimi Zohar, Jarkko Sakkinen, keyrings, David Howells

[-- Attachment #1: Type: text/plain, Size: 2675 bytes --]

On Fri, 2021-05-21 at 08:55 -0700, James Bottomley wrote:
> On Fri, 2021-05-21 at 16:22 +0100, David Woodhouse wrote:
> > On Fri, 2021-05-21 at 07:28 -0700, James Bottomley wrote:
> > > On Fri, 2021-05-21 at 14:48 +0100, David Woodhouse wrote:
> > > > On Thu, 2021-05-20 at 17:43 -0700, James Bottomley wrote:
> > > > > Now that the ASN.1 representation of trusted keys is upstream
> > > > > we can add policy to the keys as a sequence of policy
> > > > > statements meaning the kernel can now construct and use the
> > > > > policy session rather than the user having to do it and pass
> > > > > the session down to the kernel.  This makes TPM 2.0 keys with
> > > > > policy much easier.
> > > > > 
> > > > > The format of the policy statements is compatible with the
> > > > > openssl_tpm2_engine policy implementation:
> > > > > 
> > > > > https://git.kernel.org/pub/scm/linux/kernel/git/jejb/openssl_tpm2_engine.git/
> > > > > 
> > > > > And the seal_tpm2_data command in the above can be used to
> > > > > create sealed keys (including with policy statements) for the
> > > > > kernel.
> > > > 
> > > > I'd love to see that format properly defined and documented
> > > > instead of just a reference to another implementation.
> > > 
> > > Well if you want to help me write an RFC, I can try to submit it.
> > 
> > The xml2rfc tool makes it fairly easy.
> 
> XML and easy ... there's two words you don't often see in a sentence
> ...
> 
> > See https://github.com/dwmw2/ietf-cert-best-practice for a template;
> > in Appendix B there is an example of specifying an ASN.1 format.
> 
> OK, I'll add it to 
> 
> https://git.kernel.org/pub/scm/linux/kernel/git/jejb/openssl_tpm2_engine.git/
> 
> But I'll be expecting patches ...

Absolutely :)

> > We should probably define not just the ASN.1 format but also a URI
> > scheme for referencing objects in NVRAM. A TPMv2 version of 
> > https://datatracker.ietf.org/doc/html/draft-mavrogiannopoulos-tpmuri-01
> > might be a good idea.
> 
> I'm not so sure ... the keys are files not tokens and the pkcs11 URI
> doesn't have a file pointer.  I suspect the correct way to represent
> them would be to fully represent the key in the URI, which sounds like
> a length explosion.

Not files, and definitely nothing to do with PKCS#11.

I meant a URI for referring to keys which are in NVRAM. The kind you
currently use the '//nvkey:' prefix for.

We should standardise that form, as a URI, so that users can take that
same URI to *any* application and expect it to work. That's what 
https://tools.ietf.org/html/draft-mavrogiannopoulos-tpmuri-01 was
doing, for TPMv1.2.

[-- Attachment #2: smime.p7s --]
[-- Type: application/x-pkcs7-signature, Size: 5174 bytes --]

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 0/4] Trusted Key policy for TPM 2.0
  2021-05-21 16:12         ` David Woodhouse
@ 2021-05-21 16:17           ` James Bottomley
  2021-05-21 17:53             ` David Woodhouse
  0 siblings, 1 reply; 15+ messages in thread
From: James Bottomley @ 2021-05-21 16:17 UTC (permalink / raw)
  To: David Woodhouse, linux-integrity
  Cc: Mimi Zohar, Jarkko Sakkinen, keyrings, David Howells

On Fri, 2021-05-21 at 17:12 +0100, David Woodhouse wrote:
> On Fri, 2021-05-21 at 08:55 -0700, James Bottomley wrote:
> > On Fri, 2021-05-21 at 16:22 +0100, David Woodhouse wrote:
[...]
> > > We should probably define not just the ASN.1 format but also a
> > > URI scheme for referencing objects in NVRAM. A TPMv2 version of 
> > > https://datatracker.ietf.org/doc/html/draft-mavrogiannopoulos-tpmuri-01
> > > might be a good idea.
> > 
> > I'm not so sure ... the keys are files not tokens and the pkcs11
> > URI doesn't have a file pointer.  I suspect the correct way to
> > represent them would be to fully represent the key in the URI,
> > which sounds like a length explosion.
> 
> Not files, and definitely nothing to do with PKCS#11.
> 
> I meant a URI for referring to keys which are in NVRAM. The kind you
> currently use the '//nvkey:' prefix for.
> 
> We should standardise that form, as a URI, so that users can take
> that same URI to *any* application and expect it to work. That's
> what https://tools.ietf.org/html/draft-mavrogiannopoulos-tpmuri-01
> was doing, for TPMv1.2.

I'm not so sure we want to encourage that.  The persistent handle space
is really limited in TPM 2.0.  We just ran into a real world situation
where the TPM ran out after a handful.  It was an application that
loaded files into persistent handles ("because it's easier") and then
made use of them ... we're currently fixing it not to use persistent
handles because it doesn't need to.

James



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 0/4] Trusted Key policy for TPM 2.0
  2021-05-21 16:17           ` James Bottomley
@ 2021-05-21 17:53             ` David Woodhouse
  0 siblings, 0 replies; 15+ messages in thread
From: David Woodhouse @ 2021-05-21 17:53 UTC (permalink / raw)
  To: James Bottomley, linux-integrity
  Cc: Mimi Zohar, Jarkko Sakkinen, keyrings, David Howells

[-- Attachment #1: Type: text/plain, Size: 960 bytes --]

On Fri, 2021-05-21 at 09:17 -0700, James Bottomley wrote:
> I'm not so sure we want to encourage that.  The persistent handle space
> is really limited in TPM 2.0.  We just ran into a real world situation
> where the TPM ran out after a handful.  It was an application that
> loaded files into persistent handles ("because it's easier") and then
> made use of them ... we're currently fixing it not to use persistent
> handles because it doesn't need to.

Makes sense. We should fix StrongSwan then, because they're doing the
same thing.

https://wiki.strongswan.org/projects/strongswan/wiki/TpmPlugin

Of course, if we document the file format and make it ubiquitously
supported (including making an OpenSSL *provider* to replace the
obsolete ENGINEs, and chasing it into GnuTLS in 
https://gitlab.com/gnutls/gnutls/-/issues/594 ), that will go a long
way towards encouraging applications to use keys wrapped in files
instead of NVRAM...


[-- Attachment #2: smime.p7s --]
[-- Type: application/x-pkcs7-signature, Size: 5174 bytes --]

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 1/4] security: keys: trusted: add PCR policy to TPM2 keys
  2021-05-21  0:43 ` [PATCH 1/4] security: keys: trusted: add PCR policy to TPM2 keys James Bottomley
@ 2021-05-22 22:38   ` Jarkko Sakkinen
  0 siblings, 0 replies; 15+ messages in thread
From: Jarkko Sakkinen @ 2021-05-22 22:38 UTC (permalink / raw)
  To: James Bottomley
  Cc: linux-integrity, Mimi Zohar, David Woodhouse, keyrings, David Howells

On Thu, May 20, 2021 at 05:43:58PM -0700, James Bottomley wrote:
> This commit adds the ability to specify a PCR lock policy to TPM2
> keys.  There is a complexity in that the creator of the key must chose
> either to use a PCR lock policy or to use authentication.  At the
> current time they can't use both due to a complexity with the way
> authentication works when policy registers are in use.  The way to
> construct a pcrinfo statement for a key is simply to use the
> TPMS_PCR_SELECT structure to specify the PCRs and follow this by a
> hash of all their values in order of ascending PCR number.
> 
> For simplicity, we require the hash of the PCRs to use the same hash
> algorithm as the one passed in.  Thus to construct a policy around the
> value of the resettable PCR 16 using the sha1 bank, first reset the
> PCR to zero giving a hash of all zeros as:
> 
> 6768033e216468247bd031a0a2d9876d79818f8f
> 
> Then the TPMS_PCR_SELECT value for PCR 16 is
> 
> 03000001
> 
> So create a new 32 byte key with a policy locking the key to this
> value of PCR 16 with a parent key of 81000001 would be:
> 
> keyctl add trusted kmk "new 32 keyhandle=0x81000001 hash=sha1 pcrinfo=030000016768033e216468247bd031a0a2d9876d79818f8f" @u
> 
> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>

API looks sane.

> ---
>  .../security/keys/trusted-encrypted.rst       |  36 +-
>  include/keys/trusted-type.h                   |   5 +-
>  include/linux/tpm.h                           |   3 +
>  security/keys/Kconfig                         |   2 +
>  security/keys/trusted-keys/Makefile           |   1 +
>  security/keys/trusted-keys/tpm2-policy.c      | 359 ++++++++++++++++++
>  security/keys/trusted-keys/tpm2-policy.h      |  30 ++
>  security/keys/trusted-keys/tpm2key.asn1       |  13 +
>  security/keys/trusted-keys/trusted_core.c     |   8 +-
>  security/keys/trusted-keys/trusted_tpm2.c     |  86 ++++-
>  10 files changed, 530 insertions(+), 13 deletions(-)
>  create mode 100644 security/keys/trusted-keys/tpm2-policy.c
>  create mode 100644 security/keys/trusted-keys/tpm2-policy.h
> 
> diff --git a/Documentation/security/keys/trusted-encrypted.rst b/Documentation/security/keys/trusted-encrypted.rst
> index 80d5a5af62a1..5c66f29b7a1c 100644
> --- a/Documentation/security/keys/trusted-encrypted.rst
> +++ b/Documentation/security/keys/trusted-encrypted.rst
> @@ -156,7 +156,10 @@ Usage::
>                       (40 ascii zeros)
>         blobauth=     ascii hex auth for sealed data default 0x00...
>                       (40 ascii zeros)
> -       pcrinfo=	     ascii hex of PCR_INFO or PCR_INFO_LONG (no default)
> +       pcrinfo=      ascii hex of PCR_INFO or PCR_INFO_LONG (no
> +                     default) on TPM 1.2 and a TPMS_PCR_SELECTION
> +                     coupled with a hash of all the selected PCRs on
> +                     TPM 2.0 using the selected hash.
>         pcrlock=	     pcr number to be extended to "lock" blob
>         migratable=   0|1 indicating permission to reseal to new PCR values,
>                       default 1 (resealing allowed)
> @@ -254,6 +257,20 @@ Load a trusted key from the saved blob::
>      f1f8fff03ad0acb083725535636addb08d73dedb9832da198081e5deae84bfaf0409c22b
>      e4a8aea2b607ec96931e6f4d4fe563ba
>  
> +Create a trusted key on TPM 2.0 using an all zero value of PCR16 and
> +using the NV storage root 81000001 as the parent::
> +
> +    $ keyctl add trusted kmk "new 32 keyhandle=0x81000001 hash=sha1 pcrinfo=030000016768033e216468247bd031a0a2d9876d79818f8f" @u
> +
> +Note the TPMS_PCR_SELECT value for PCR 16 is 03000001 because all
> +current TPMs have 24 PCRs, so the initial 03 says there are three
> +following bytes of selection and then because the bytes are big
> +endian, 16 is bit zero of byte 2. the hash is the sha1 sum of all
> +zeros (the value of PCR 16)::
> +
> +    $ dd if=/dev/zero bs=1 count=20 2>/dev/null|sha1sum
> +    6768033e216468247bd031a0a2d9876d79818f8f
> +
>  Reseal (TPM specific) a trusted key under new PCR values::
>  
>      $ keyctl update 268728824 "update pcrinfo=`cat pcr.blob`"
> @@ -325,11 +342,17 @@ policy::
>      TPMKey ::= SEQUENCE {
>          type		OBJECT IDENTIFIER
>          emptyAuth	[0] EXPLICIT BOOLEAN OPTIONAL
> +        policy		[1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL
>          parent		INTEGER
>          pubkey		OCTET STRING
>          privkey		OCTET STRING
>      }
>  
> +    TPMPolicy ::= SEQUENCE {
> +	CommandCode		[0] EXPLICIT INTEGER
> +	CommandPolicy		[1] EXPLICIT OCTET STRING
> +    }
> +
>  type is what distinguishes the key even in binary form since the OID
>  is provided by the TCG to be unique and thus forms a recognizable
>  binary pattern at offset 3 in the key.  The OIDs currently made
> @@ -355,6 +378,17 @@ is false or not present, the key requires an explicit authorization
>  phrase.  This is used by most user space consumers to decide whether
>  to prompt for a password.
>  
> +policy represents a sequence of one or more policy statements that
> +must be executed successfully into a session policy register.  If
> +policy isn't present then no policy is required to unlock the key, but
> +if it is, commandCode is the TPM 2.0 command code of the policy
> +instruction that must be executed and CommandPolicy represents the
> +binary parameter area of the policy command.
> +
> +Note that the current sequential execution requirement means that only
> +AND based policy can be constructed at the moment, so TPM2_PolicyOR is
> +not currently supported.
> +
>  parent represents the parent key handle, either in the 0x81 MSO space,
>  like 0x81000001 for the RSA primary storage key.  Userspace programmes
>  also support specifying the primary handle in the 0x40 MSO space.  If
> diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h
> index d89fa2579ac0..749dadbd72e7 100644
> --- a/include/keys/trusted-type.h
> +++ b/include/keys/trusted-type.h
> @@ -20,9 +20,11 @@
>  #define MIN_KEY_SIZE			32
>  #define MAX_KEY_SIZE			128
>  #define MAX_BLOB_SIZE			512
> -#define MAX_PCRINFO_SIZE		64
> +#define MAX_PCRINFO_SIZE		128
>  #define MAX_DIGEST_SIZE			64
>  
> +#define TPM2_MAX_POLICIES		16
> +
>  struct trusted_key_payload {
>  	struct rcu_head rcu;
>  	unsigned int key_len;
> @@ -31,6 +33,7 @@ struct trusted_key_payload {
>  	unsigned char old_format;
>  	unsigned char key[MAX_KEY_SIZE + 1];
>  	unsigned char blob[MAX_BLOB_SIZE];
> +	struct tpm2_policies *policies;
>  };
>  
>  struct trusted_key_options {
> diff --git a/include/linux/tpm.h b/include/linux/tpm.h
> index aa11fe323c56..298321fe07ee 100644
> --- a/include/linux/tpm.h
> +++ b/include/linux/tpm.h
> @@ -230,10 +230,12 @@ enum tpm2_command_codes {
>  	TPM2_CC_CONTEXT_LOAD	        = 0x0161,
>  	TPM2_CC_CONTEXT_SAVE	        = 0x0162,
>  	TPM2_CC_FLUSH_CONTEXT	        = 0x0165,
> +	TPM2_CC_START_AUTH_SESS		= 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,
> @@ -242,6 +244,7 @@ enum tpm2_command_codes {
>  };
>  
>  enum tpm2_permanent_handles {
> +	TPM2_RH_NULL		= 0x40000007,
>  	TPM2_RS_PW		= 0x40000009,
>  };
>  
> diff --git a/security/keys/Kconfig b/security/keys/Kconfig
> index 64b81abd087e..8a79f4faba97 100644
> --- a/security/keys/Kconfig
> +++ b/security/keys/Kconfig
> @@ -74,6 +74,8 @@ config TRUSTED_KEYS
>  	select CRYPTO
>  	select CRYPTO_HMAC
>  	select CRYPTO_SHA1
> +	select CRYPTO_SHA256
> +	select CRYPTO_SHA512
>  	select CRYPTO_HASH_INFO
>  	select ASN1_ENCODER
>  	select OID_REGISTRY
> diff --git a/security/keys/trusted-keys/Makefile b/security/keys/trusted-keys/Makefile
> index feb8b6c3cc79..39c598c4d2ef 100644
> --- a/security/keys/trusted-keys/Makefile
> +++ b/security/keys/trusted-keys/Makefile
> @@ -10,5 +10,6 @@ trusted-y += trusted_tpm1.o
>  $(obj)/trusted_tpm2.o: $(obj)/tpm2key.asn1.h
>  trusted-y += trusted_tpm2.o
>  trusted-y += tpm2key.asn1.o
> +trusted-y += tpm2-policy.o
>  
>  trusted-$(CONFIG_TEE) += trusted_tee.o
> diff --git a/security/keys/trusted-keys/tpm2-policy.c b/security/keys/trusted-keys/tpm2-policy.c
> new file mode 100644
> index 000000000000..b05b2953d5ea
> --- /dev/null
> +++ b/security/keys/trusted-keys/tpm2-policy.c
> @@ -0,0 +1,359 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2019 James.Bottomley@HansenPartnership.com
> + */
> +
> +#include <linux/asn1_encoder.h>
> +#include <linux/err.h>
> +#include <linux/types.h>
> +#include <linux/printk.h>
> +#include <linux/string.h>
> +#include <linux/tpm.h>
> +
> +#include <asm/unaligned.h>
> +
> +#include <crypto/hash.h>
> +
> +#include <keys/trusted-type.h>
> +#include <keys/trusted_tpm.h>
> +
> +#include "tpm2key.asn1.h"
> +#include "tpm2-policy.h"
> +
> +/* used by ASN.1 parser only to extract policy code */
> +int tpm2_key_code(void *context, size_t hdrlen,
> +		  unsigned char tag,
> +		  const void *value, size_t vlen)
> +{

Why the function name is tpm2_key_code() but the comment phrases
"policy code". Some sort of verb of action would be nice, e.g.
tpm2_extract_*_code(), expecially since this isn't idempotent
function.

And prototype can realigned to two lines, instead of three.

> +	struct tpm2_key_context *ctx = context;
> +	u32 code = 0;
> +	const u8 *v = value;

const u8 *v = value;
u32 code = 0;

> +	int i;
> +
> +	for (i = 0; i < vlen; i++) {
> +		code <<= 8;
> +		code |= v[i];
> +	}
> +
> +	ctx->policy_code[ctx->policy_count] = code;
> +
> +	return 0;
> +}
> +
> +/* used by ASN.1 parser only to extract policy sequence */
> +int tpm2_key_policy(void *context, size_t hdrlen,
> +		  unsigned char tag,
> +		  const void *value, size_t vlen)

Ditto.

> +{
> +	struct tpm2_key_context *ctx = context;
> +
> +	ctx->policies[ctx->policy_count] = value;
> +	ctx->policy_len[ctx->policy_count++] = vlen;
> +
> +	return 0;
> +}
> +
> +/* we only support a limited number of policy statement so
> + * make sure we don't have anything we can't support
> + */

I'd prefer if sentences are always written properly, i.e. so that
they start with a capital etc.

Also, the comment should be

/*
 * Text
 */

according to kdoc documentation.

> +static int tpm2_validate_policy(struct tpm2_policies *pols)
> +{
> +	int i;
> +
> +	if (pols->count == 0)
> +		return 0;
> +
> +	for (i = 0; i < pols->count; i++) {
> +		switch (pols->code[i]) {
> +		case TPM2_CC_POLICY_PCR:
> +			break;
> +		default:
> +			pr_warn("tpm2 policy 0x%x is unsupported",

Acronym is written incorrectly. It should be "TPM2". Also, since this isn't
any sort of kernel issue when it happens, this should be pr_info().

It's also lacking "\n".

> +				pols->code[i]);
> +			return -EINVAL;
> +		}

The switch-case statement serves zero purpose here, i.e. please change to:

        if (pols->code[i] != TPM2_CC_POLICY_PCR)
                pr_info("TPM2 policy 0x%x is unsupported.\n", pols->code[i]);

> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * tpm2_key_process_policy - collect the policty from the context
> + * @ctx: the context to collect from
> + * @payload: the payload structure to place it in

Should be aligned with tabs:

https://www.kernel.org/doc/Documentation/kernel-doc-nano-HOWTO.txt

Also, I'm wondering why this has a verb in the function name, while
earlier didn't. There's no coherence.

> + *
> + * THis function sizes the policy statements and allocates space

"This"

> + * within the payload to receive them before copying them over.  It
> + * should be used after the ber decoder has completed successfully
> + */
> +int tpm2_key_policy_process(struct tpm2_key_context *ctx,
> +			    struct trusted_key_payload *payload)
> +{
> +	int tot_len = 0;
> +	u8 *buf;
> +	int i, ret, len = 0;
> +	struct tpm2_policies *pols;
> +
> +	if (ctx->policy_count == 0)
> +		return 0;
> +
> +	for (i = 0; i < ctx->policy_count; i++)
> +		tot_len += ctx->policy_len[i];
> +	tot_len += sizeof(*pols);
> +
> +	pols = kmalloc(tot_len, GFP_KERNEL);
> +	if (!pols)
> +		return -ENOMEM;
> +
> +	payload->policies = pols;
> +	buf = (u8 *)(pols + 1);
> +
> +	for (i = 0; i < ctx->policy_count; i++) {
> +		pols->policies[i] = &buf[len];
> +		pols->len[i] = ctx->policy_len[i];
> +		pols->code[i] = ctx->policy_code[i];
> +		if (pols->len[i])
> +			memcpy(pols->policies[i], ctx->policies[i],
> +			       ctx->policy_len[i]);
> +		len += ctx->policy_len[i];
> +	}
> +	pols->count = ctx->policy_count;
> +
> +	ret = tpm2_validate_policy(pols);
> +	if (ret) {
> +		kfree(pols);
> +		payload->policies = NULL;
> +	}
> +
> +	/* capture the hash and size */

Quite useless comment.

> +
> +	/* the hash is the second algorithm */
> +	pols->hash = get_unaligned_be16(&ctx->pub[4]);
> +	/* and the digest appears after the attributes */
> +	pols->hash_size = get_unaligned_be16(&ctx->pub[10]);

Would be nice to have comments written both proper form and language, e.g.

/* The hash is the second algorithm. */
pols->hash = get_unaligned_be16(&ctx->pub[4]);

/* The digest appears after the attributes. */
pols->hash_size = get_unaligned_be16(&ctx->pub[10]);

(also the empty line in-between is essential to make comments readable)

> +
> +	return ret;
> +}
> +
> +int tpm2_generate_policy_digest(struct tpm2_policies *pols,
> +				u32 hash, u8 *policydigest, u32 *plen)
> +{
> +	int i;
> +	struct crypto_shash *tfm;
> +	int rc;
> +
> +	if (pols->count == 0)
> +		return 0;
> +
> +	tfm = crypto_alloc_shash(hash_algo_name[hash], 0, 0);
> +	if (IS_ERR(tfm))
> +		return PTR_ERR(tfm);
> +
> +	rc = crypto_shash_digestsize(tfm);
> +	if (WARN(rc > MAX_DIGEST_SIZE,
> +		 "BUG: trusted key code has alg %s with digest too large (%d)",
> +		 hash_algo_name[hash], rc)) {
> +		rc = -EINVAL;
> +		goto err;
> +	}
> +
> +	pols->hash = hash;
> +	pols->hash_size = rc;
> +	*plen = rc;
> +
> +	/* policy digests always start out all zeros */
> +	memset(policydigest, 0, rc);
> +
> +	for (i = 0; i < pols->count; i++) {
> +		u8 *policy = pols->policies[i];
> +		int len = pols->len[i];
> +		u32 cmd = pols->code[i];
> +		u8 code[4];
> +		SHASH_DESC_ON_STACK(sdesc, tfm);
> +
> +		sdesc->tfm = tfm;
> +		rc = crypto_shash_init(sdesc);
> +		if (rc)
> +			goto err;
> +
> +		/* first hash the previous digest */
> +		crypto_shash_update(sdesc, policydigest, *plen);
> +
> +		/* then hash the command code */
> +		put_unaligned_be32(cmd, code);
> +		crypto_shash_update(sdesc, code, 4);
> +
> +		crypto_shash_update(sdesc, policy, len);
> +
> +		/* now output the intermediate to the policydigest */
> +		crypto_shash_final(sdesc, policydigest);
> +
> +	}
> +	rc = 0;
> +
> + err:
> +	crypto_free_shash(tfm);
> +	return rc;
> +}
> +
> +int tpm2_encode_policy(struct tpm2_policies *pols, u8 **data, u32 *len)
> +{
> +	const int SCRATCH_SIZE = PAGE_SIZE;
> +	u8 *buf = kmalloc(2 * SCRATCH_SIZE, GFP_KERNEL);
> +	u8 *work = buf + SCRATCH_SIZE;
> +	u8 *ptr;
> +	u8 *end_work = work + SCRATCH_SIZE;
> +	int i, ret;
> +
> +	if (!buf)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < pols->count; i++) {
> +		u8 *seq, *tag;
> +		u32 cmd = pols->code[i];
> +
> +		if (WARN(work - buf + 14 + pols->len[i] > 2 * SCRATCH_SIZE,
> +			 "BUG: scratch buffer is too small"))
> +			return -EINVAL;
> +
> +		work = asn1_encode_sequence(work, end_work, NULL, -1);
> +		seq = work;
> +
> +		work = asn1_encode_tag(work, end_work, 0, NULL, -1);
> +		tag = work;
> +
> +		work = asn1_encode_integer(work, end_work, cmd);
> +		asn1_encode_tag(tag, end_work, 0, NULL, work - tag);
> +
> +		work = asn1_encode_tag(work, end_work, 1, NULL, -1);
> +		tag = work;
> +
> +		work = asn1_encode_octet_string(work, end_work,
> +						pols->policies[i],
> +						pols->len[i]);
> +
> +		asn1_encode_tag(tag, end_work, 1, NULL, work - tag);
> +
> +		seq = asn1_encode_sequence(seq, end_work, NULL, work - seq);
> +		if (IS_ERR(seq)) {
> +			ret = PTR_ERR(seq);
> +			goto err;
> +		}
> +	}
> +	ptr = asn1_encode_sequence(buf, buf + SCRATCH_SIZE, buf + PAGE_SIZE,
> +				   work - buf - PAGE_SIZE);
> +	if (IS_ERR(ptr)) {
> +		ret = PTR_ERR(ptr);
> +		goto err;
> +	}
> +
> +	*data = buf;
> +	*len = ptr - buf;
> +
> +	return 0;
> +
> + err:
> +	kfree(buf);
> +	return ret;
> +}
> +
> +static int tpm2_start_policy_session(struct tpm_chip *chip,
> +				     u16 hash, u32 *handle)
> +{
> +	struct tpm_buf buf;
> +	int rc;
> +	int i;
> +
> +	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_START_AUTH_SESS);
> +	if (rc)
> +		return rc;
> +
> +	/* NULL salt key handle */
> +	tpm_buf_append_u32(&buf, TPM2_RH_NULL);
> +
> +	/* NULL bind key handle */
> +	tpm_buf_append_u32(&buf, TPM2_RH_NULL);
> +
> +	/* empty nonce caller */
> +	tpm_buf_append_u16(&buf, 20);
> +	for (i = 0; i < 20; i++)
> +		tpm_buf_append_u8(&buf, 0);
> +
> +	/* empty auth */
> +	tpm_buf_append_u16(&buf, 0);
> +
> +	/* session type policy */
> +	tpm_buf_append_u8(&buf, 0x01);
> +
> +	/* symmetric encryption parameters */
> +
> +	/* symmetric algorithm  */
> +	tpm_buf_append_u16(&buf, TPM_ALG_NULL);
> +
> +	/* hash algorithm for session */
> +	tpm_buf_append_u16(&buf, hash);
> +
> +	rc = tpm_transmit_cmd(chip, &buf, 0, "start policy session");
> +	if (rc)
> +		goto out;
> +
> +	*handle = get_unaligned_be32(buf.data + TPM_HEADER_SIZE);
> +
> + out:
> +	tpm_buf_destroy(&buf);
> +
> +	return rc <= 0 ? rc : -EPERM;
> +}
> +
> +int tpm2_get_policy_session(struct tpm_chip *chip, struct tpm2_policies *pols,
> +			    u32 *handle)
> +{
> +	int i, rc;
> +	const char *failure;
> +
> +	rc = tpm2_start_policy_session(chip, pols->hash, handle);
> +	if (rc)
> +		return rc;
> +
> +	for (i = 0; i < pols->count; i++) {
> +		u32 cmd = pols->code[i];
> +		struct tpm_buf buf;
> +
> +		rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, cmd);
> +		if (rc)
> +			return rc;
> +
> +		tpm_buf_append_u32(&buf, *handle);
> +
> +		switch (cmd) {
> +		case TPM2_CC_POLICY_PCR:
> +			failure = "PCR";
> +			/*
> +			 * for reasons best known to the TCG we have
> +			 * to reverse the two arguments to send to the
> +			 * policy command
> +			 */
> +			tpm_buf_append_u16(&buf, pols->hash_size);
> +			tpm_buf_append(&buf, pols->policies[i] + pols->len[i] -
> +				       pols->hash_size, pols->hash_size);
> +			tpm_buf_append(&buf, pols->policies[i],
> +				       pols->len[i] - pols->hash_size);
> +			break;
> +		default:
> +			failure = "unknown policy";
> +			break;
> +		}
> +
> +		rc = tpm_transmit_cmd(chip, &buf, 0, "send policy");
> +		tpm_buf_destroy(&buf);
> +		if (rc) {
> +			dev_err(&chip->dev, "TPM policy %s failed, rc=%d\n",
> +				failure, rc);
> +			tpm2_flush_context(chip, *handle);
> +			*handle = 0;
> +			return -EPERM;
> +		}
> +	}
> +
> +	return 0;
> +}
> diff --git a/security/keys/trusted-keys/tpm2-policy.h b/security/keys/trusted-keys/tpm2-policy.h
> new file mode 100644
> index 000000000000..46bf1f0a9325
> --- /dev/null
> +++ b/security/keys/trusted-keys/tpm2-policy.h
> @@ -0,0 +1,30 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +
> +struct tpm2_key_context {
> +	u32 parent;
> +	const u8 *pub;
> +	u32 pub_len;
> +	const u8 *priv;
> +	u32 priv_len;
> +	const u8 *policies[TPM2_MAX_POLICIES];
> +	u32 policy_code[TPM2_MAX_POLICIES];
> +	u16 policy_len[TPM2_MAX_POLICIES];
> +	u8 policy_count;
> +};
> +
> +struct tpm2_policies {
> +	u32 code[TPM2_MAX_POLICIES];
> +	u8 *policies[TPM2_MAX_POLICIES];
> +	u16 len[TPM2_MAX_POLICIES];
> +	u8 count;
> +	u16 hash;
> +	u16 hash_size;
> +};
> +
> +int tpm2_key_policy_process(struct tpm2_key_context *ctx,
> +			    struct trusted_key_payload *payload);
> +int tpm2_generate_policy_digest(struct tpm2_policies *pols, u32 hash,
> +				u8 *policydigest, u32 *plen);
> +int tpm2_encode_policy(struct tpm2_policies *pols, u8 **data, u32 *len);
> +int tpm2_get_policy_session(struct tpm_chip *chip, struct tpm2_policies *pols,
> +			    u32 *handle);
> diff --git a/security/keys/trusted-keys/tpm2key.asn1 b/security/keys/trusted-keys/tpm2key.asn1
> index f57f869ad600..1684bd8f725e 100644
> --- a/security/keys/trusted-keys/tpm2key.asn1
> +++ b/security/keys/trusted-keys/tpm2key.asn1
> @@ -1,11 +1,24 @@
>  ---
>  --- ASN.1 for TPM 2.0 keys
>  ---
> +--- Note: This isn't quite the definition in the standard
> +---       However, the Linux asn.1 parser doesn't understand
> +---       [2] EXPLICIT SEQUENCE OF OPTIONAL
> +---       So there's an extra intermediate TPMPolicySequence
> +---       definition to work around this
>  
>  TPMKey ::= SEQUENCE {
>  	type		OBJECT IDENTIFIER ({tpm2_key_type}),
>  	emptyAuth	[0] EXPLICIT BOOLEAN OPTIONAL,
> +	policy		[1] EXPLICIT TPMPolicySequence OPTIONAL,
>  	parent		INTEGER ({tpm2_key_parent}),
>  	pubkey		OCTET STRING ({tpm2_key_pub}),
>  	privkey		OCTET STRING ({tpm2_key_priv})
>  	}
> +
> +TPMPolicySequence ::= SEQUENCE OF TPMPolicy
> +
> +TPMPolicy ::= SEQUENCE {
> +	commandCode		[0] EXPLICIT INTEGER ({tpm2_key_code}),
> +	commandPolicy		[1] EXPLICIT OCTET STRING ({tpm2_key_policy})
> +	}
> diff --git a/security/keys/trusted-keys/trusted_core.c b/security/keys/trusted-keys/trusted_core.c
> index d5c891d8d353..7a41c64e67b0 100644
> --- a/security/keys/trusted-keys/trusted_core.c
> +++ b/security/keys/trusted-keys/trusted_core.c
> @@ -209,6 +209,7 @@ static void trusted_rcu_free(struct rcu_head *rcu)
>  	struct trusted_key_payload *p;
>  
>  	p = container_of(rcu, struct trusted_key_payload, rcu);
> +	kfree_sensitive(p->policies);
>  	kfree_sensitive(p);
>  }
>  
> @@ -299,7 +300,12 @@ static long trusted_read(const struct key *key, char *buffer,
>   */
>  static void trusted_destroy(struct key *key)
>  {
> -	kfree_sensitive(key->payload.data[0]);
> +
> +	struct trusted_key_payload *p;
> +
> +	p = key->payload.data[0];
> +	kfree_sensitive(p->policies);
> +	kfree_sensitive(p);
>  }
>  
>  struct key_type key_type_trusted = {
> diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
> index 0165da386289..a218f982fef5 100644
> --- a/security/keys/trusted-keys/trusted_tpm2.c
> +++ b/security/keys/trusted-keys/trusted_tpm2.c
> @@ -17,6 +17,7 @@
>  #include <asm/unaligned.h>
>  
>  #include "tpm2key.asn1.h"
> +#include "tpm2-policy.h"
>  
>  static struct tpm2_hash tpm2_hash_map[] = {
>  	{HASH_ALGO_SHA1, TPM_ALG_SHA1},
> @@ -62,6 +63,21 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
>  		work = asn1_encode_tag(work, end_work, 0, bool, w - bool);
>  	}
>  
> +	if (payload->policies) {
> +		u8 *encoded_pols;
> +		u32 encoded_pol_len;
> +		int ret;
> +
> +		ret = tpm2_encode_policy(payload->policies, &encoded_pols,
> +					 &encoded_pol_len);
> +		if (ret)
> +			return ret;
> +
> +		work = asn1_encode_tag(work, end_work, 1, encoded_pols,
> +				       encoded_pol_len);
> +		kfree(encoded_pols);
> +	}
> +
>  	/*
>  	 * Assume both octet strings will encode to a 2 byte definite length
>  	 *
> @@ -85,14 +101,6 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
>  	return work1 - payload->blob;
>  }
>  
> -struct tpm2_key_context {
> -	u32 parent;
> -	const u8 *pub;
> -	u32 pub_len;
> -	const u8 *priv;
> -	u32 priv_len;
> -};
> -
>  static int tpm2_key_decode(struct trusted_key_payload *payload,
>  			   struct trusted_key_options *options,
>  			   u8 **buf)
> @@ -115,6 +123,12 @@ static int tpm2_key_decode(struct trusted_key_payload *payload,
>  	if (!blob)
>  		return -ENOMEM;
>  
> +	ret = tpm2_key_policy_process(&ctx, payload);
> +	if (ret) {
> +		kfree(blob);
> +		return ret;
> +	}
> +
>  	*buf = blob;
>  	options->keyhandle = ctx.parent;
>  
> @@ -248,6 +262,42 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
>  	if (!options->keyhandle)
>  		return -EINVAL;
>  
> +	if (options->pcrinfo_len != 0) {
> +		struct tpm2_policies *pols;
> +		static u8 *scratch;
> +		/* 4 array len, 2 hash alg */
> +		const int len = 4 + 2 + options->pcrinfo_len;
> +
> +		pols = kmalloc(sizeof(*pols) + len, GFP_KERNEL);
> +		if (!pols)
> +			return -ENOMEM;
> +
> +		pols->count = 1;
> +		pols->len[0] = len;
> +		scratch = (u8 *)(pols + 1);
> +		pols->policies[0] = scratch;
> +		pols->code[0] = TPM2_CC_POLICY_PCR;
> +
> +		put_unaligned_be32(1, &scratch[0]);
> +		put_unaligned_be16(hash, &scratch[4]);
> +		memcpy(&scratch[6], options->pcrinfo, options->pcrinfo_len);
> +		payload->policies = pols;
> +	}
> +
> +	if (options->policydigest_len != 0 && payload->policies) {
> +		/* can't specify both a digest and a policy */
> +		return -EINVAL;
> +	}
> +
> +	if (payload->policies) {
> +		rc = tpm2_generate_policy_digest(payload->policies,
> +						 options->hash,
> +						 options->policydigest,
> +						 &options->policydigest_len);
> +		if (rc)
> +			return rc;
> +	}
> +
>  	rc = tpm_try_get_ops(chip);
>  	if (rc)
>  		return rc;
> @@ -464,21 +514,37 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
>  	u16 data_len;
>  	u8 *data;
>  	int rc;
> +	u32 policyhandle;
> +
> +	if (payload->policies && options->policyhandle)
> +		/* can't have both a passed in policy and a key resident one */
> +		return -EINVAL;
>  
>  	rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
>  	if (rc)
>  		return rc;
>  
> +	if (payload->policies) {
> +		rc = tpm2_get_policy_session(chip, payload->policies,
> +					     &policyhandle);
> +		if (rc)
> +			return rc;
> +	} else {
> +		policyhandle = options->policyhandle;
> +	}
> +
>  	tpm_buf_append_u32(&buf, blob_handle);
>  	tpm2_buf_append_auth(&buf,
> -			     options->policyhandle ?
> -			     options->policyhandle : TPM2_RS_PW,
> +			     policyhandle ?
> +			     policyhandle : TPM2_RS_PW,
>  			     NULL /* nonce */, 0,
>  			     TPM2_SA_CONTINUE_SESSION,
>  			     options->blobauth /* hmac */,
>  			     options->blobauth_len);
>  
>  	rc = tpm_transmit_cmd(chip, &buf, 6, "unsealing");
> +	if (payload->policies)
> +		tpm2_flush_context(chip, policyhandle);

Empty line (probably other similar places scattered here and there).

>  	if (rc > 0)
>  		rc = -EPERM;
>  
> -- 
> 2.26.2
> 
> 

I did not check everything. I think the API looks decent, just patches
need a lot of polishing.

/Jarkko

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 2/4] security: keys: trusted: add ability to specify arbitrary policy
  2021-05-21  0:43 ` [PATCH 2/4] security: keys: trusted: add ability to specify arbitrary policy James Bottomley
@ 2021-05-22 22:40   ` Jarkko Sakkinen
  0 siblings, 0 replies; 15+ messages in thread
From: Jarkko Sakkinen @ 2021-05-22 22:40 UTC (permalink / raw)
  To: James Bottomley
  Cc: linux-integrity, Mimi Zohar, David Woodhouse, keyrings, David Howells

On Thu, May 20, 2021 at 05:43:59PM -0700, James Bottomley wrote:
> This patch adds a policy= argument to key creation.  The policy is the
> standard tss policymaker format and each separate policy line must
> have a newline after it.
> 
> Thus to construct a policy requiring authorized value and pcr 16
> locking using a sha256 hash, the policy (policy.txt) file would be two
> lines:
> 
> 0000017F00000001000B03000001303095B49BE85E381E5B20E557E46363EF55B0F43B132C2D8E3DE9AC436656F2
> 0000016b
> 
> This can be inserted into the key with
> 
> keyctl add trusted kmk "new 32 policy=`cat policy.txt` keyhandle=0x81000001 hash=sha256" @u
> 
> Note that although a few policies work like this, most require special
> handling which must be added to the kernel policy construction
> routine.
> 
> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
> ---
>  .../security/keys/trusted-encrypted.rst       | 16 ++++++
>  security/keys/trusted-keys/tpm2-policy.c      | 53 +++++++++++++++++++
>  security/keys/trusted-keys/tpm2-policy.h      |  1 +
>  security/keys/trusted-keys/trusted_tpm1.c     | 15 ++++++
>  security/keys/trusted-keys/trusted_tpm2.c     |  4 ++
>  5 files changed, 89 insertions(+)
> 
> diff --git a/Documentation/security/keys/trusted-encrypted.rst b/Documentation/security/keys/trusted-encrypted.rst
> index 5c66f29b7a1c..883844c95e91 100644
> --- a/Documentation/security/keys/trusted-encrypted.rst
> +++ b/Documentation/security/keys/trusted-encrypted.rst
> @@ -172,6 +172,9 @@ 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.
> +       policy=       specify an arbitrary set of policies.  These must
> +                     be in policymaker format with each separate
> +                     policy line newline terminated.
>  
>  "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.
> @@ -271,6 +274,19 @@ zeros (the value of PCR 16)::
>      $ dd if=/dev/zero bs=1 count=20 2>/dev/null|sha1sum
>      6768033e216468247bd031a0a2d9876d79818f8f
>  
> +You can also specify arbitrary policy in policymaker format, so a two
> +value policy (the pcr example above and authvalue) would look like
> +this in policymaker format::
> +
> +    0000017F000000010004030000016768033e216468247bd031a0a2d9876d79818f8f
> +    0000016b
> +
> +This can be placed in a file (say policy.txt) and then added to the key as::
> +
> +    $ keyctl add trusted kmk "new 32 keyhandle=0x81000001 hash=sha1 policy=`cat policy.txt`" @u
> +
> +The newlines in the file policy.txt will be automatically processed.
> +
>  Reseal (TPM specific) a trusted key under new PCR values::
>  
>      $ keyctl update 268728824 "update pcrinfo=`cat pcr.blob`"
> diff --git a/security/keys/trusted-keys/tpm2-policy.c b/security/keys/trusted-keys/tpm2-policy.c
> index b05b2953d5ea..bb28c864fa9b 100644
> --- a/security/keys/trusted-keys/tpm2-policy.c
> +++ b/security/keys/trusted-keys/tpm2-policy.c
> @@ -357,3 +357,56 @@ int tpm2_get_policy_session(struct tpm_chip *chip, struct tpm2_policies *pols,
>  
>  	return 0;
>  }
> +
> +int tpm2_parse_policies(struct tpm2_policies **ppols, char *str)
> +{
> +	struct tpm2_policies *pols;
> +	char *p;
> +	u8 *ptr;
> +	int i = 0, left = PAGE_SIZE, res;
> +
> +	pols = kmalloc(left, GFP_KERNEL);
> +	if (!pols)
> +		return -ENOMEM;
> +
> +	ptr = (u8 *)(pols + 1);
> +	left -= ptr - (u8 *)pols;
> +
> +	while ((p = strsep(&str, "\n"))) {
> +		if (*p == '\0' || *p == '\n')
> +			continue;
> +
> +		pols->len[i] = strlen(p)/2;
> +		if (pols->len[i] > left) {
> +			res = -E2BIG;
> +			goto err;
> +		}
> +
> +		res = hex2bin(ptr, p, pols->len[i]);
> +		if (res)
> +			goto err;
> +
> +		/* get command code and skip past */
> +		pols->code[i] = get_unaligned_be32(ptr);
> +		pols->policies[i] = ptr + 4;
> +		ptr += pols->len[i];
> +		left -= pols->len[i];
> +		pols->len[i] -= 4;
> +
> +		/*
> +		 * FIXME: this does leave the code embedded in dead
> +		 * regions of the memory, but it's easier than
> +		 * hexdumping to a temporary or copying over

What is "this"?

> +		 */
> +		i++;
> +	}
> +
> +	pols->count = i;
> +	*ppols = pols;
> +
> +	return 0;
> +
> + err:
> +	kfree(pols);
> +	return res;
> +}
> diff --git a/security/keys/trusted-keys/tpm2-policy.h b/security/keys/trusted-keys/tpm2-policy.h
> index 46bf1f0a9325..0da013116c1c 100644
> --- a/security/keys/trusted-keys/tpm2-policy.h
> +++ b/security/keys/trusted-keys/tpm2-policy.h
> @@ -28,3 +28,4 @@ int tpm2_generate_policy_digest(struct tpm2_policies *pols, u32 hash,
>  int tpm2_encode_policy(struct tpm2_policies *pols, u8 **data, u32 *len);
>  int tpm2_get_policy_session(struct tpm_chip *chip, struct tpm2_policies *pols,
>  			    u32 *handle);
> +int tpm2_parse_policies(struct tpm2_policies **ppols, char *str);
> diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
> index aa108bea6739..6ed7303e36b5 100644
> --- a/security/keys/trusted-keys/trusted_tpm1.c
> +++ b/security/keys/trusted-keys/trusted_tpm1.c
> @@ -22,6 +22,8 @@
>  
>  #include <keys/trusted_tpm.h>
>  
> +#include "tpm2-policy.h"
> +
>  static const char hmac_alg[] = "hmac(sha1)";
>  static const char hash_alg[] = "sha1";
>  static struct tpm_chip *chip;
> @@ -713,6 +715,7 @@ enum {
>  	Opt_hash,
>  	Opt_policydigest,
>  	Opt_policyhandle,
> +	Opt_policy,
>  };
>  
>  static const match_table_t key_tokens = {
> @@ -725,6 +728,7 @@ static const match_table_t key_tokens = {
>  	{Opt_hash, "hash=%s"},
>  	{Opt_policydigest, "policydigest=%s"},
>  	{Opt_policyhandle, "policyhandle=%s"},
> +	{Opt_policy, "policy=%s"},
>  	{Opt_err, NULL}
>  };
>  
> @@ -858,6 +862,17 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
>  				return -EINVAL;
>  			opt->policyhandle = handle;
>  			break;
> +
> +		case Opt_policy:
> +			if (pay->policies)
> +				return -EINVAL;
> +			if (!tpm2)
> +				return -EINVAL;
> +			res = tpm2_parse_policies(&pay->policies, args[0].from);
> +			if (res)
> +				return res;
> +			break;
> +
>  		default:
>  			return -EINVAL;
>  		}
> diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
> index a218f982fef5..afe9cc41885e 100644
> --- a/security/keys/trusted-keys/trusted_tpm2.c
> +++ b/security/keys/trusted-keys/trusted_tpm2.c
> @@ -268,6 +268,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
>  		/* 4 array len, 2 hash alg */
>  		const int len = 4 + 2 + options->pcrinfo_len;
>  
> +		if (payload->policies)
> +			/* can't specify pcr and general policy */
> +			return -EINVAL;
> +
>  		pols = kmalloc(sizeof(*pols) + len, GFP_KERNEL);
>  		if (!pols)
>  			return -ENOMEM;
> -- 
> 2.26.2
> 
> 

/Jarkko

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 0/4] Trusted Key policy for TPM 2.0
  2021-05-21 13:48 ` [PATCH 0/4] Trusted Key policy for TPM 2.0 David Woodhouse
  2021-05-21 14:28   ` James Bottomley
@ 2021-05-22 22:45   ` Jarkko Sakkinen
  1 sibling, 0 replies; 15+ messages in thread
From: Jarkko Sakkinen @ 2021-05-22 22:45 UTC (permalink / raw)
  To: David Woodhouse
  Cc: James Bottomley, linux-integrity, Mimi Zohar, keyrings, David Howells

On Fri, May 21, 2021 at 02:48:55PM +0100, David Woodhouse wrote:
> On Thu, 2021-05-20 at 17:43 -0700, James Bottomley wrote:
> > Now that the ASN.1 representation of trusted keys is upstream we can
> > add policy to the keys as a sequence of policy statements meaning the
> > kernel can now construct and use the policy session rather than the
> > user having to do it and pass the session down to the kernel.  This
> > makes TPM 2.0 keys with policy much easier.
> > 
> > The format of the policy statements is compatible with the
> > openssl_tpm2_engine policy implementation:
> > 
> > https://git.kernel.org/pub/scm/linux/kernel/git/jejb/openssl_tpm2_engine.git/
> > 
> > And the seal_tpm2_data command in the above can be used to create
> > sealed keys (including with policy statements) for the kernel.
> 
> I'd love to see that format properly defined and documented instead of
> just a reference to another implementation.

A valid point. How can we know that this is good for "everyone"?

Also, one major thing that this patch set is kselftest's. It's too
obnoxious to copy-paste examples from commit messages, and also
does not really create a good platform to discuss any possible
issues that these patches might have.

/Jarkko

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2021-05-22 22:45 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-21  0:43 [PATCH 0/4] Trusted Key policy for TPM 2.0 James Bottomley
2021-05-21  0:43 ` [PATCH 1/4] security: keys: trusted: add PCR policy to TPM2 keys James Bottomley
2021-05-22 22:38   ` Jarkko Sakkinen
2021-05-21  0:43 ` [PATCH 2/4] security: keys: trusted: add ability to specify arbitrary policy James Bottomley
2021-05-22 22:40   ` Jarkko Sakkinen
2021-05-21  0:44 ` [PATCH 3/4] security: keys: trusted: implement counter/timer policy James Bottomley
2021-05-21  0:44 ` [PATCH 4/4] security: keys: trusted: implement authorization policy James Bottomley
2021-05-21 13:48 ` [PATCH 0/4] Trusted Key policy for TPM 2.0 David Woodhouse
2021-05-21 14:28   ` James Bottomley
2021-05-21 15:22     ` David Woodhouse
2021-05-21 15:55       ` James Bottomley
2021-05-21 16:12         ` David Woodhouse
2021-05-21 16:17           ` James Bottomley
2021-05-21 17:53             ` David Woodhouse
2021-05-22 22:45   ` Jarkko Sakkinen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).