On Fri May 24, 2024 at 4:04 PM EEST, James Bottomley wrote:
> Signed policy allows key policies to be modified after the TPM key is
> created, provided the new policy is signed with a private key whose
> public part is encoded into the initial policy.  How this works is
> described in
>
> https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html
>
> Since this means that the key structure now contains public keys and
> signatures, the maximum blob size has to be expanded simply to
> accommodate a key with several policies.  The code assumes (as does
> all other signed policy engines) that the first policy is the most
> likely one to succeed, so it tries all the available signed policies
> in first to last order and loads the key when one of them succeeds.
>
> This code allows the kernel to process signed policy keys correctly,
> it does not allow the kernel to create them.
>
> Signed-off-by: James Bottomley <james.bottom...@hansenpartnership.com>
> ---
>  drivers/char/tpm/tpm2-sessions.c          |   6 +
>  include/keys/trusted-type.h               |   3 +-
>  include/linux/tpm.h                       |   6 +
>  security/keys/trusted-keys/tpm2-policy.c  | 473 ++++++++++++++++------
>  security/keys/trusted-keys/tpm2-policy.h  |  19 +-
>  security/keys/trusted-keys/tpm2key.asn1   |  12 +-
>  security/keys/trusted-keys/trusted_tpm2.c |  63 +--
>  7 files changed, 434 insertions(+), 148 deletions(-)
>
> diff --git a/drivers/char/tpm/tpm2-sessions.c 
> b/drivers/char/tpm/tpm2-sessions.c
> index 63c175b2165c..c2b73283c46f 100644
> --- a/drivers/char/tpm/tpm2-sessions.c
> +++ b/drivers/char/tpm/tpm2-sessions.c
> @@ -1365,3 +1365,9 @@ int tpm2_sessions_init(struct tpm_chip *chip)
>  
>       return rc;
>  }
> +
> +struct crypto_shash *tpm2_get_policy_hash(struct tpm_chip *chip)
> +{
> +     return chip->auth->tfm;
> +}

Please open code.


> +EXPORT_SYMBOL(tpm2_get_policy_hash);
> diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h
> index 5d1d481a8a19..a6999800055e 100644
> --- a/include/keys/trusted-type.h
> +++ b/include/keys/trusted-type.h
> @@ -19,11 +19,12 @@
>  
>  #define MIN_KEY_SIZE                 32
>  #define MAX_KEY_SIZE                 128
> -#define MAX_BLOB_SIZE                        512
> +#define MAX_BLOB_SIZE                        4096
>  #define MAX_PCRINFO_SIZE             128
>  #define MAX_DIGEST_SIZE                      64
>  
>  #define TPM2_MAX_POLICIES            16
> +#define TPM2_MAX_AUTHS                       8
>  
>  struct trusted_key_payload {
>       struct rcu_head rcu;
> diff --git a/include/linux/tpm.h b/include/linux/tpm.h
> index 894e51a7fe3a..09f14482675b 100644
> --- a/include/linux/tpm.h
> +++ b/include/linux/tpm.h
> @@ -290,6 +290,8 @@ enum tpm2_command_codes {
>       TPM2_CC_CONTEXT_LOAD            = 0x0161,
>       TPM2_CC_CONTEXT_SAVE            = 0x0162,
>       TPM2_CC_FLUSH_CONTEXT           = 0x0165,
> +     TPM2_CC_LOAD_EXTERNAL           = 0x0167,
> +     TPM2_CC_POLICY_AUTHORIZE        = 0x016A,
>       TPM2_CC_POLICY_AUTHVALUE        = 0x016B,
>       TPM2_CC_POLICY_COUNTER_TIMER    = 0x016D,
>       TPM2_CC_READ_PUBLIC             = 0x0173,
> @@ -299,14 +301,17 @@ enum tpm2_command_codes {
>       TPM2_CC_GET_RANDOM              = 0x017B,
>       TPM2_CC_PCR_READ                = 0x017E,
>       TPM2_CC_POLICY_PCR              = 0x017F,
> +     TPM2_CC_POLICY_RESTART          = 0x0180,
>       TPM2_CC_PCR_EXTEND              = 0x0182,
>       TPM2_CC_EVENT_SEQUENCE_COMPLETE = 0x0185,
>       TPM2_CC_HASH_SEQUENCE_START     = 0x0186,
> +     TPM2_CC_POLICY_GET_DIGEST       = 0x0189,
>       TPM2_CC_CREATE_LOADED           = 0x0191,
>       TPM2_CC_LAST                    = 0x0193, /* Spec 1.36 */
>  };
>  
>  enum tpm2_permanent_handles {
> +     TPM2_RH_OWNER           = 0x40000001,
>       TPM2_RH_NULL            = 0x40000007,
>       TPM2_RS_PW              = 0x40000009,
>  };
> @@ -578,6 +583,7 @@ static inline void tpm_buf_append_empty_auth(struct 
> tpm_buf *buf, u32 handle)
>  
>  int tpm2_start_auth_session(struct tpm_chip *chip);
>  int tpm2_start_policy_session(struct tpm_chip *chip, u32 *handle, u8 hash);
> +struct crypto_shash *tpm2_get_policy_hash(struct tpm_chip *chip);
>  void tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
>                        u32 handle, u8 *name);
>  void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
> diff --git a/security/keys/trusted-keys/tpm2-policy.c 
> b/security/keys/trusted-keys/tpm2-policy.c
> index c0508cb95923..f9ee57fed8fe 100644
> --- a/security/keys/trusted-keys/tpm2-policy.c
> +++ b/security/keys/trusted-keys/tpm2-policy.c
> @@ -28,13 +28,28 @@ int tpm2_key_code(void *context, size_t hdrlen,
>       u32 code = 0;
>       const u8 *v = value;
>       int i;
> +     u8 a = ctx->auth_policies;
>  
>       for (i = 0; i < vlen; i++) {
>               code <<= 8;
>               code |= v[i];
>       }
>  
> -     ctx->policy_code[ctx->policy_count] = code;
> +     ctx->policy_code[a][ctx->policy_count[a]] = code;
> +
> +     return 0;
> +}
> +
> +int tpm2_pol_seq(void *context, size_t hdrlen,
> +              unsigned char tag,
> +              const void *value, size_t vlen)
> +{
> +     struct tpm2_key_context *ctx = context;
> +
> +     ctx->auth_policies++;
> +
> +     if (ctx->auth_policies > TPM2_MAX_AUTHS)
> +             return -EINVAL;
>  
>       return 0;
>  }
> @@ -44,9 +59,15 @@ int tpm2_key_policy(void *context, size_t hdrlen,
>                 const void *value, size_t vlen)
>  {
>       struct tpm2_key_context *ctx = context;
> +     u8 a = ctx->auth_policies;
> +     u8 policy_count = ctx->policy_count[a]++;
> +
> +     if (policy_count >= TPM2_MAX_POLICIES)
> +             return -EINVAL;
>  
> -     ctx->policies[ctx->policy_count] = value;
> -     ctx->policy_len[ctx->policy_count++] = vlen;
> +     ctx->policies[a][policy_count] = value;
> +     ctx->policy_len[a][policy_count] = vlen;
> +     ctx->policy_tot_len += vlen;
>  
>       return 0;
>  }
> @@ -56,21 +77,24 @@ int tpm2_key_policy(void *context, size_t hdrlen,
>   */
>  static int tpm2_validate_policy(struct tpm2_policies *pols)
>  {
> -     int i;
> +     int i, j;
>  
> -     if (pols->count == 0)
> +     if (pols->auths == 0)
>               return 0;
>  
> -     for (i = 0; i < pols->count; i++) {
> -             switch (pols->code[i]) {
> -             case TPM2_CC_POLICY_COUNTER_TIMER:
> -             case TPM2_CC_POLICY_PCR:
> -             case TPM2_CC_POLICY_AUTHVALUE:
> -                     break;
> -             default:
> -                     pr_info("tpm2 policy 0x%x is unsupported\n",
> -                             pols->code[i]);
> +     for (i = 0; i < pols->auths; i++) {
> +             for (j = 0; j < pols->count[i]; j++) {
> +                     switch (pols->code[i][j]) {
> +                     case TPM2_CC_POLICY_COUNTER_TIMER:
> +                     case TPM2_CC_POLICY_PCR:
> +                     case TPM2_CC_POLICY_AUTHVALUE:
> +                     case TPM2_CC_POLICY_AUTHORIZE:
> +                             break;
> +                     default:
> +                             pr_info("tpm2 policy 0x%x is unsupported\n",
> +                                     pols->code[i][j]);
>                       return -EINVAL;
> +                     }
>               }
>       }
>  
> @@ -89,36 +113,35 @@ static int tpm2_validate_policy(struct tpm2_policies 
> *pols)
>  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;
> +     int i, j, ret, len = 0;
>       struct tpm2_policies *pols;
>       u16 name_alg;
>  
> -     if (ctx->policy_count == 0)
> +     if (ctx->policy_tot_len == 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);
> +     pols = kmalloc(ctx->policy_tot_len + sizeof(*pols), 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];
> +     for (i = 0; i < ctx->auth_policies; i++) {
> +             for (j = 0; j < ctx->policy_count[i]; j++) {
> +                     pols->policies[i][j] = &buf[len];
> +                     pols->len[i][j] = ctx->policy_len[i][j];
> +                     pols->code[i][j] = ctx->policy_code[i][j];
> +                     if (pols->len[i][j])
> +                             memcpy(pols->policies[i][j],
> +                                    ctx->policies[i][j],
> +                                    ctx->policy_len[i][j]);
> +                     len += ctx->policy_len[i][j];
> +             }
> +             pols->count[i] = ctx->policy_count[i];
>       }
> -     pols->count = ctx->policy_count;
> +     pols->auths = ctx->auth_policies;
>  
>       ret = tpm2_validate_policy(pols);
>       if (ret)
> @@ -148,7 +171,7 @@ int tpm2_key_policy_process(struct tpm2_key_context *ctx,
>  int tpm2_generate_policy_digest(struct tpm2_policies *pols,
>                               u32 hash, u8 *policydigest, u32 *plen)
>  {
> -     int i;
> +     int i, j;
>       struct crypto_shash *tfm;
>       int rc;
>  
> @@ -174,45 +197,47 @@ int tpm2_generate_policy_digest(struct tpm2_policies 
> *pols,
>       /* 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 digest[MAX_DIGEST_SIZE];
> -             u8 code[4];
> -             SHASH_DESC_ON_STACK(sdesc, tfm);
> +     for (i = 0; i < pols->auths; i++) {
> +             for (j = 0; j < pols->count[i]; j++) {
> +                     u8 *policy = pols->policies[i][j];
> +                     int len = pols->len[i][j];
> +                     u32 cmd = pols->code[i][j];
> +                     u8 digest[MAX_DIGEST_SIZE];
> +                     u8 code[4];
> +                     SHASH_DESC_ON_STACK(sdesc, tfm);
>  
> -             sdesc->tfm = tfm;
> -             rc = crypto_shash_init(sdesc);
> -             if (rc)
> -                     goto err;
> +                     sdesc->tfm = tfm;
> +                     rc = crypto_shash_init(sdesc);
> +                     if (rc)
> +                             goto err;
>  
> -             /* first hash the previous digest */
> -             crypto_shash_update(sdesc, policydigest, *plen);
> +                     /* 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);
> +                     /* then hash the command code */
> +                     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);
> +                     /* commands that need special handling */
> +                     if (cmd == TPM2_CC_POLICY_COUNTER_TIMER) {
> +                             SHASH_DESC_ON_STACK(sdesc1, tfm);
>  
> -                     sdesc1->tfm = tfm;
> +                             sdesc1->tfm = tfm;
>  
> -                     /* counter timer policies are double hashed */
> -                     crypto_shash_digest(sdesc1, policy, len,
> -                                         digest);
> -                     policy = digest;
> -                     len = *plen;
> -             }
> +                             /* counter timer policies are double hashed */
> +                             crypto_shash_digest(sdesc1, policy, len,
> +                                                 digest);
> +                             policy = digest;
> +                             len = *plen;
> +                     }
>  
> -             if (len)
> -                     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);
> +                     /* now output the intermediate to the policydigest */
> +                     crypto_shash_final(sdesc, policydigest);
>  
> +             }
>       }
>       rc = 0;
>  
> @@ -228,41 +253,45 @@ int tpm2_encode_policy(struct tpm2_policies *pols, u8 
> **data, u32 *len)
>       u8 *work = buf + SCRATCH_SIZE;
>       u8 *ptr;
>       u8 *end_work = work + SCRATCH_SIZE;
> -     int i, ret;
> +     int i, j, ret;
>  
>       if (!buf)
>               return -ENOMEM;
>  
> -     for (i = 0; i < pols->count; i++) {
> -             u8 *seq, *tag;
> -             u32 cmd = pols->code[i];
> +     for (i = 0; i < pols->auths; i++) {
> +             for (j = 0; j < pols->count[i]; j++) {
> +                     u8 *seq, *tag;
> +                     u32 cmd = pols->code[i][j];
>  
> -             if (WARN(work - buf + 14 + pols->len[i] > 2 * SCRATCH_SIZE,
> -                      "BUG: scratch buffer is too small"))
> -                     return -EINVAL;
> +                     if (WARN(work - buf + 14 + pols->len[i][j] >
> +                              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_sequence(work, end_work, NULL, -1);
> +                     seq = work;
>  
> -             work = asn1_encode_tag(work, end_work, 0, NULL, -1);
> -             tag = 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_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_tag(work, end_work, 1, NULL, -1);
> +                     tag = work;
>  
> -             work = asn1_encode_octet_string(work, end_work,
> -                                             pols->policies[i],
> -                                             pols->len[i]);
> +                     work = asn1_encode_octet_string(work, end_work,
> +                                                     pols->policies[i][j],
> +                                                     pols->len[i][j]);
>  
> -             asn1_encode_tag(tag, end_work, 1, NULL, work - tag);
> +                     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;
> +                     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,
> @@ -282,18 +311,131 @@ int tpm2_encode_policy(struct tpm2_policies *pols, u8 
> **data, u32 *len)
>       return ret;
>  }
>  
> -int tpm2_get_policy_session(struct tpm_chip *chip, struct tpm2_policies 
> *pols)
> +static int tpm2_policy_restart(struct tpm_chip *chip, u32 handle)
>  {
> -     int i, rc;
> -     u32 handle;
> -     const char *failure;
> +     struct tpm_buf buf;
> +     int rc;
>  
> -     rc = tpm2_start_policy_session(chip, &handle, pols->hash);
> +     rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_POLICY_RESTART);
> +     if (rc)
> +             return rc;
> +
> +     tpm_buf_append_u32(&buf, handle);
> +
> +     rc = tpm_transmit_cmd(chip, &buf, 0, "restarting policy");
> +     tpm_buf_destroy(&buf);
> +     if (rc) {
> +             pr_notice("TPM policy restart failed, rc=%d\n", rc);
> +             return -EPERM;
> +     }
> +     return 0;
> +}
> +
> +static int tpm2_policy_gettkhash(struct tpm_chip *chip, u8 *pubkey, u8 
> *nonce,
> +                              u8 *signature, int siglen,
> +                              u32 handle, u8 **hash, u8 **name,
> +                              u8 **ticket)
> +{
> +     struct tpm_buf buf;
> +     int rc;
> +     int len;
> +     u32 sigkey;
> +     off_t offset;
> +     SHASH_DESC_ON_STACK(sdesc, tpm2_get_policy_hash(chip));
> +
> +     sdesc->tfm = tpm2_get_policy_hash(chip);
> +
> +     rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_POLICY_GET_DIGEST);
>       if (rc)
>               return rc;
>  
> -     for (i = 0; i < pols->count; i++) {
> -             u32 cmd = pols->code[i];
> +     tpm_buf_append_u32(&buf, handle);
> +
> +     rc = tpm_transmit_cmd(chip, &buf, 0, "getting policy hash");
> +     if (rc)
> +             goto out;
> +     len = get_unaligned_be16(&buf.data[TPM_HEADER_SIZE]) + 2;
> +     rc = -ENOMEM;
> +     *hash = kmalloc(len, GFP_KERNEL);
> +     if (!*hash)
> +             goto out;
> +     memcpy(*hash, &buf.data[TPM_HEADER_SIZE], len);
> +
> +     tpm_buf_reset(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_LOAD_EXTERNAL);
> +
> +     /* empty sensitive */
> +     tpm_buf_append_u16(&buf, 0);
> +     /* TPM2B_PUBLIC */
> +     len = get_unaligned_be16(pubkey) + 2;
> +     tpm_buf_append(&buf, pubkey, len);
> +     /* hierarchy (use null because never a password) */
> +     tpm_buf_append_u32(&buf, TPM2_RH_OWNER);
> +
> +     rc = tpm_transmit_cmd(chip, &buf, 4, "loading external key");
> +     if (rc)
> +             goto out;
> +
> +     offset = TPM_HEADER_SIZE;
> +     sigkey = tpm_buf_read_u32(&buf, &offset);
> +     len = get_unaligned_be16(&buf.data[offset]) + 2;
> +     *name = kmalloc(len, GFP_KERNEL);
> +     if (!*name) {
> +             tpm2_flush_context(chip, sigkey);
> +             rc = -ENOMEM;
> +             goto out;
> +     }
> +     memcpy(*name, &buf.data[offset], len);
> +
> +     tpm_buf_reset(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_VERIFY_SIGNATURE);
> +     /* handle of public key to verify with */
> +     tpm_buf_append_u32(&buf, sigkey);
> +     /* digest hash(policy||nonce) */
> +     len = get_unaligned_be16(*hash);
> +     tpm_buf_append_u16(&buf, len);
> +     /* now compute the signed data which is hash(policy||nonce) */
> +     crypto_shash_init(sdesc);
> +     len = get_unaligned_be16(*hash); /* better be the tfm hash size */
> +     crypto_shash_update(sdesc, *hash + 2, len);
> +     len = get_unaligned_be16(nonce);
> +     crypto_shash_update(sdesc, nonce + 2, len);
> +     crypto_shash_final(sdesc, &buf.data[buf.length]);
> +     buf.length += len;
> +     /* signature */
> +     tpm_buf_append(&buf, signature, siglen);
> +
> +     rc = tpm_transmit_cmd(chip, &buf, 4, "verifying signature");
> +     tpm2_flush_context(chip, sigkey);
> +     if (rc)
> +             goto out;
> +
> +     len = tpm_buf_length(&buf) - TPM_HEADER_SIZE;
> +     *ticket = kmalloc(len, GFP_KERNEL);
> +     if (!*ticket) {
> +             rc = -ENOMEM;
> +             goto out;
> +     }
> +     memcpy(*ticket, &buf.data[TPM_HEADER_SIZE], len);
> +
> + out:
> +     if (rc) {
> +             kfree(*hash);
> +             *hash = NULL;
> +             kfree(*name);
> +             *name = NULL;
> +     }
> +     tpm_buf_destroy(&buf);
> +     return rc;
> +}
> +
> +
> +static int tpm2_try_policy(struct tpm_chip *chip, struct tpm2_policies *pols,
> +                        u32 handle, int p)
> +{
> +     int i, rc;
> +     const char *failure;
> +
> +     for (i = 0; i < pols->count[p]; i++) {
> +             u32 cmd = pols->code[p][i];
>               struct tpm_buf buf;
>  
>               rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, cmd);
> @@ -311,10 +453,11 @@ int tpm2_get_policy_session(struct tpm_chip *chip, 
> struct tpm2_policies *pols)
>                        * policy command
>                        */
>                       tpm_buf_append_u16(&buf, pols->hash_size);
> -                     tpm_buf_append(&buf, pols->policies[i] + pols->len[i] -
> +                     tpm_buf_append(&buf, pols->policies[p][i]
> +                                    + pols->len[p][i] -
>                                      pols->hash_size, pols->hash_size);
> -                     tpm_buf_append(&buf, pols->policies[i],
> -                                    pols->len[i] - pols->hash_size);
> +                     tpm_buf_append(&buf, pols->policies[p][i],
> +                                    pols->len[p][i] - pols->hash_size);
>                       break;
>  
>               case TPM2_CC_POLICY_COUNTER_TIMER: {
> @@ -324,26 +467,82 @@ int tpm2_get_policy_session(struct tpm_chip *chip, 
> struct tpm2_policies *pols)
>                        * respectively.  The rest is operandB which
>                        * must be zero padded in a hash digest
>                        */
> -                     u16 opb_len = pols->len[i] - 4;
> +                     u16 opb_len = pols->len[p][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);
> +                     tpm_buf_append(&buf, pols->policies[p][i], opb_len);
>  
>                       /* offset and operand*/
> -                     tpm_buf_append(&buf, pols->policies[i] + opb_len, 4);
> +                     tpm_buf_append(&buf, pols->policies[p][i] + opb_len, 4);
>                       failure = "Counter Timer";
>  
>                       break;
>               }
> +             case TPM2_CC_POLICY_AUTHORIZE: {
> +                     u8 *pubkey = pols->policies[p][i];
> +                     u8 *nonce;
> +                     u8 *signature;
> +                     u8 *ticket = NULL;
> +                     u8 *hash = NULL;
> +                     u8 *name = NULL;
> +                     int len, siglen;
> +                     const int maxlen = pols->len[p][i];
> +
> +                     if (i == 0)
> +                             /*
> +                              * If this is the first policy then skip
> +                              * because we should already have executed
> +                              * a successful PolicyAuthorize before getting
> +                              * here
> +                              */
> +                             continue;
> +
> +                     len = get_unaligned_be16(pubkey);
> +                     if (len + 2 > maxlen) {
> +                             failure = "malformed policy";
> +                             break;
> +                     }
> +                     nonce = pubkey + len + 2;
> +                     len = get_unaligned_be16(nonce);
> +                     if (len + 2 > maxlen) {
> +                             failure = "malformed policy";
> +                             break;
> +                     }
> +                     signature = nonce + len + 2;
> +                     siglen = pubkey + maxlen - signature;
> +                     failure = "policy authorize";
> +                     rc = tpm2_policy_gettkhash(chip, pubkey, nonce,
> +                                                signature, siglen,
> +                                                handle, &hash,
> +                                                &name, &ticket);
> +                     if (rc)
> +                             break;
> +                     len = get_unaligned_be16(hash);
> +                     tpm_buf_append(&buf, hash, len + 2);
> +                     kfree(hash);
> +
> +                     len = get_unaligned_be16(nonce);
> +                     tpm_buf_append(&buf, nonce, len + 2);
> +
> +                     len = get_unaligned_be16(name);
> +                     tpm_buf_append(&buf, name, len + 2);
> +                     kfree(name);
> +
> +                     len = get_unaligned_be16(ticket + 6) + 8;
> +                     tpm_buf_append(&buf, ticket, len);
> +                     kfree(ticket);
> +
> +                     break;
> +             }
>  
>               default:
>                       failure = "unknown policy";
> -                     if (pols->len[i])
> -                             tpm_buf_append(&buf, pols->policies[i],
> -                                            pols->len[i]);
> +                     if (pols->len[p][i])
> +                             tpm_buf_append(&buf, pols->policies[p][i],
> +                                            pols->len[p][i]);
>  
>                       break;
>               }
> @@ -353,14 +552,62 @@ int tpm2_get_policy_session(struct tpm_chip *chip, 
> struct tpm2_policies *pols)
>               if (rc) {
>                       pr_notice("TPM policy %s failed, rc=%d\n",
>                                 failure, rc);
> -                     tpm2_end_auth_session(chip);
> -                     return -EPERM;
> +                     return rc;
>               }
>       }
> -
>       return 0;
>  }
>  
> +int tpm2_get_policy_session(struct tpm_chip *chip, struct tpm2_policies 
> *pols)
> +{
> +     int rc;
> +     u32 handle;
> +
> +     if (pols->code[0][0] == TPM2_CC_POLICY_AUTHORIZE &&
> +         pols->auths == 1) {
> +             pr_notice("TPM Key requires signed policies but has none\n");
> +             return -EINVAL;
> +     }
> +
> +     rc = tpm2_start_policy_session(chip, &handle, pols->hash);
> +     if (rc)
> +             return rc;
> +
> +     if (pols->code[0][0] == TPM2_CC_POLICY_AUTHORIZE) {
> +             int p;
> +
> +             for (p = 1; p < pols->auths; p++) {
> +                     if (p != 1) {
> +                             /* restart policy if looping */
> +                             rc = tpm2_policy_restart(chip, handle);
> +                             if (rc)
> +                                     goto out;
> +                     }
> +
> +                     rc = tpm2_try_policy(chip, pols, handle, p);
> +                     if (rc) {
> +                             pr_notice("TPM signed policy %d failed\n", p);
> +                     } else {
> +                             pr_notice("TPM signed policy %d succeeded\n",
> +                                       p);
> +                             break;
> +                     }
> +             }
> +             if (rc)
> +                     /* no signed policies succeeded */
> +                     goto out;
> +     }
> +
> +     rc = tpm2_try_policy(chip, pols, handle, 0);
> + out:
> +     if (rc) {
> +             rc = -EPERM;
> +             tpm2_end_auth_session(chip);
> +     }
> +
> +     return rc;
> +}
> +
>  int tpm2_parse_policies(struct tpm2_policies **ppols, char *str)
>  {
>       struct tpm2_policies *pols;
> @@ -379,22 +626,22 @@ int tpm2_parse_policies(struct tpm2_policies **ppols, 
> char *str)
>               if (*p == '\0' || *p == '\n')
>                       continue;
>  
> -             pols->len[i] = strlen(p)/2;
> -             if (pols->len[i] > left) {
> +             pols->len[0][i] = strlen(p)/2;
> +             if (pols->len[0][i] > left) {
>                       res = -E2BIG;
>                       goto err;
>               }
>  
> -             res = hex2bin(ptr, p, pols->len[i]);
> +             res = hex2bin(ptr, p, pols->len[0][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;
> +             pols->code[0][i] = get_unaligned_be32(ptr);
> +             pols->policies[0][i] = ptr + 4;
> +             ptr += pols->len[0][i];
> +             left -= pols->len[0][i];
> +             pols->len[0][i] -= 4;
>  
>               /*
>                * FIXME: this does leave the code embedded in dead
> @@ -404,7 +651,7 @@ int tpm2_parse_policies(struct tpm2_policies **ppols, 
> char *str)
>               i++;
>       }
>  
> -     pols->count = i;
> +     pols->count[0] = i;
>       *ppols = pols;
>  
>       return 0;
> diff --git a/security/keys/trusted-keys/tpm2-policy.h 
> b/security/keys/trusted-keys/tpm2-policy.h
> index 8ddf235b3fec..da0ab99078b8 100644
> --- a/security/keys/trusted-keys/tpm2-policy.h
> +++ b/security/keys/trusted-keys/tpm2-policy.h
> @@ -6,17 +6,20 @@ struct tpm2_key_context {
>       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;
> +     const u8 *policies[TPM2_MAX_AUTHS][TPM2_MAX_POLICIES];
> +     u32 policy_code[TPM2_MAX_AUTHS][TPM2_MAX_POLICIES];
> +     u16 policy_len[TPM2_MAX_AUTHS][TPM2_MAX_POLICIES];
> +     u8 policy_count[TPM2_MAX_AUTHS];
> +     u8 auth_policies;
> +     int policy_tot_len;
>  };
>  
>  struct tpm2_policies {
> -     u32 code[TPM2_MAX_POLICIES];
> -     u8 *policies[TPM2_MAX_POLICIES];
> -     u16 len[TPM2_MAX_POLICIES];
> -     u8 count;
> +     u32 code[TPM2_MAX_AUTHS][TPM2_MAX_POLICIES];
> +     u8 *policies[TPM2_MAX_AUTHS][TPM2_MAX_POLICIES];
> +     u16 len[TPM2_MAX_AUTHS][TPM2_MAX_POLICIES];
> +     u8 count[TPM2_MAX_AUTHS];
> +     u8 auths;
>       int hash;               /* crypto not TPM hash algorithm */
>       u16 hash_size;
>  };
> diff --git a/security/keys/trusted-keys/tpm2key.asn1 
> b/security/keys/trusted-keys/tpm2key.asn1
> index 1684bd8f725e..c5a68b3e354f 100644
> --- a/security/keys/trusted-keys/tpm2key.asn1
> +++ b/security/keys/trusted-keys/tpm2key.asn1
> @@ -5,12 +5,13 @@
>  ---       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
> +---       and TPMAuthPolicySequence definitions to work around this
>  
>  TPMKey ::= SEQUENCE {
>       type            OBJECT IDENTIFIER ({tpm2_key_type}),
>       emptyAuth       [0] EXPLICIT BOOLEAN OPTIONAL,
> -     policy          [1] EXPLICIT TPMPolicySequence OPTIONAL,
> +     policy          [1] EXPLICIT TPMPolicySequence OPTIONAL 
> ({tpm2_pol_seq}),
> +     authPolicy      [3] EXPLICIT TPMAuthPolicySequence OPTIONAL,
>       parent          INTEGER ({tpm2_key_parent}),
>       pubkey          OCTET STRING ({tpm2_key_pub}),
>       privkey         OCTET STRING ({tpm2_key_priv})
> @@ -22,3 +23,10 @@ TPMPolicy ::= SEQUENCE {
>       commandCode             [0] EXPLICIT INTEGER ({tpm2_key_code}),
>       commandPolicy           [1] EXPLICIT OCTET STRING ({tpm2_key_policy})
>       }
> +
> +TPMAuthPolicySequence ::= SEQUENCE OF TPMAuthPolicy ({tpm2_pol_seq})
> +
> +TPMAuthPolicy ::= SEQUENCE {
> +     name            [0] EXPLICIT UTF8String OPTIONAL,
> +     policy          [1] EXPLICIT TPMPolicySequence
> +     }
> diff --git a/security/keys/trusted-keys/trusted_tpm2.c 
> b/security/keys/trusted-keys/trusted_tpm2.c
> index 64c922bbc36c..e2d937e44274 100644
> --- a/security/keys/trusted-keys/trusted_tpm2.c
> +++ b/security/keys/trusted-keys/trusted_tpm2.c
> @@ -98,38 +98,45 @@ static int tpm2_key_decode(struct trusted_key_payload 
> *payload,
>                          u8 **buf)
>  {
>       int ret;
> -     struct tpm2_key_context ctx;
> +     struct tpm2_key_context *ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
>       u8 *blob;
>  
> -     memset(&ctx, 0, sizeof(ctx));
> +     if (!ctx)
> +             return -ENOMEM;
>  
> -     ret = asn1_ber_decoder(&tpm2key_decoder, &ctx, payload->blob,
> +     ret = asn1_ber_decoder(&tpm2key_decoder, ctx, payload->blob,
>                              payload->blob_len);
>       if (ret < 0)
> -             return ret;
> +             goto out;
>  
> -     if (ctx.priv_len + ctx.pub_len > MAX_BLOB_SIZE)
> -             return -EINVAL;
> +     if (ctx->priv_len + ctx->pub_len > MAX_BLOB_SIZE) {
> +             ret = -EINVAL;
> +             goto out;
> +     }
>  
> -     blob = kmalloc(ctx.priv_len + ctx.pub_len + 4, GFP_KERNEL);
> -     if (!blob)
> -             return -ENOMEM;
> +     blob = kmalloc(ctx->priv_len + ctx->pub_len + 4, GFP_KERNEL);
> +     if (!blob) {
> +             ret = -ENOMEM;
> +             goto out;
> +     }
>  
> -     ret = tpm2_key_policy_process(&ctx, payload);
> +     ret = tpm2_key_policy_process(ctx, payload);
>       if (ret) {
>               kfree(blob);
> -             return ret;
> +             goto out;
>       }
>  
>       *buf = blob;
> -     options->keyhandle = ctx.parent;
> +     options->keyhandle = ctx->parent;
>  
> -     memcpy(blob, ctx.priv, ctx.priv_len);
> -     blob += ctx.priv_len;
> +     memcpy(blob, ctx->priv, ctx->priv_len);
> +     blob += ctx->priv_len;
>  
> -     memcpy(blob, ctx.pub, ctx.pub_len);
> +     memcpy(blob, ctx->pub, ctx->pub_len);
>  
> -     return 0;
> + out:
> +     kfree(ctx);
> +     return ret;
>  }
>  
>  int tpm2_key_parent(void *context, size_t hdrlen,
> @@ -264,7 +271,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
>               pols = kmalloc(POLICY_SIZE, GFP_KERNEL);
>               if (!pols)
>                       return -ENOMEM;
> -             pols->count = 0;
> +             pols->count[0] = 0;
> +             pols->auths = 1;
>               payload->policies = pols;
>               scratch = (u8 *)(pols + 1);
>       }
> @@ -275,10 +283,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
>               /* 4 array len */
>               const int len = 4 + options->pcrinfo_len;
>  
> -             i = pols->count++;
> -             pols->len[i] = len;
> -             pols->policies[i] = scratch;
> -             pols->code[i] = TPM2_CC_POLICY_PCR;
> +             i = pols->count[0]++;
> +             pols->len[0][i] = len;
> +             pols->policies[0][i] = scratch;
> +             pols->code[0][i] = TPM2_CC_POLICY_PCR;
>  
>               /* only a single TPMS_PCR_SELECTION */
>               put_unaligned_be32(1, &scratch[0]);
> @@ -300,11 +308,11 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
>               int i;
>  
>               pols = payload->policies;
> -             i = pols->count++;
> +             i = pols->count[0]++;
>  
>               /* the TPM2_PolicyPassword command has no payload */
> -             pols->len[i] = 0;
> -             pols->code[i] = TPM2_CC_POLICY_AUTHVALUE;
> +             pols->len[0][i] = 0;
> +             pols->code[0][i] = TPM2_CC_POLICY_AUTHVALUE;
>       }
>  
>       if (payload->policies) {
> @@ -685,6 +693,13 @@ int __weak tpm2_key_code(void *context, size_t hdrlen,
>       return -EINVAL;
>  }
>  
> +int __weak tpm2_pol_seq(void *context, size_t hdrlen,
> +                     unsigned char tag,
> +                     const void *value, size_t vlen)
> +{
> +     return -EINVAL;
> +}
> +
>  int __weak tpm2_key_policy(void *context, size_t hdrlen,
>                 unsigned char tag,
>                 const void *value, size_t vlen)

BR, Jarkko

Reply via email to