On Thu, Jan 24, 2019 at 04:49:10PM +0100, Roberto Sassu wrote:
> Currently, tpm_pcr_extend() accepts as an input only a SHA1 digest.
> 
> This patch replaces the hash parameter of tpm_pcr_extend() with an array of
> tpm_digest structures, so that the caller can provide a digest for each PCR
> bank currently allocated in the TPM.
> 
> tpm_pcr_extend() will not extend banks for which no digest was provided,
> as it happened before this patch, but instead it requires that callers
> provide the full set of digests. Since the number of digests will always be
> chip->nr_allocated_banks, the count parameter has been removed.
> 
> Due to the API change, ima_pcr_extend() and pcrlock() have been modified.
> Since the number of allocated banks is not known in advance, the memory for
> the digests must be dynamically allocated. To avoid performance degradation
> and to avoid that a PCR extend is not done due to lack of memory, the array
> of tpm_digest structures is allocated by the users of the TPM driver at
> initialization time.
> 
> Signed-off-by: Roberto Sassu <[email protected]>
> ---
>  drivers/char/tpm/tpm-interface.c   | 30 ++++++++------------
>  drivers/char/tpm/tpm.h             |  2 +-
>  drivers/char/tpm/tpm2-cmd.c        | 10 ++-----
>  include/linux/tpm.h                |  5 ++--
>  security/integrity/ima/ima.h       |  1 +
>  security/integrity/ima/ima_init.c  | 22 +++++++++++++++
>  security/integrity/ima/ima_queue.c |  6 +++-
>  security/keys/trusted.c            | 44 ++++++++++++++++++++++++------
>  8 files changed, 82 insertions(+), 38 deletions(-)
> 
> diff --git a/drivers/char/tpm/tpm-interface.c 
> b/drivers/char/tpm/tpm-interface.c
> index eb7c79ca8a94..b52242a165e0 100644
> --- a/drivers/char/tpm/tpm-interface.c
> +++ b/drivers/char/tpm/tpm-interface.c
> @@ -478,42 +478,34 @@ EXPORT_SYMBOL_GPL(tpm_pcr_read);
>   * tpm_pcr_extend - extend a PCR value in SHA1 bank.
>   * @chip:    a &struct tpm_chip instance, %NULL for the default chip
>   * @pcr_idx: the PCR to be retrieved
> - * @hash:    the hash value used to extend the PCR value
> + * @digests: array of tpm_digest structures used to extend PCRs
>   *
> - * Note: with TPM 2.0 extends also those banks for which no digest was
> - * specified in order to prevent malicious use of those PCR banks.
> + * Note: callers must pass a digest for every allocated PCR bank, in the same
> + * order of the banks in chip->allocated_banks.
>   *
>   * Return: same as with tpm_transmit_cmd()
>   */
> -int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash)
> +int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
> +                struct tpm_digest *digests)
>  {
>       int rc;
> -     struct tpm_digest *digest_list;
>       int i;
>  
>       chip = tpm_find_get_ops(chip);
>       if (!chip)
>               return -ENODEV;
>  
> -     if (chip->flags & TPM_CHIP_FLAG_TPM2) {
> -             digest_list = kcalloc(chip->nr_allocated_banks,
> -                                   sizeof(*digest_list), GFP_KERNEL);
> -             if (!digest_list)
> -                     return -ENOMEM;
> -
> -             for (i = 0; i < chip->nr_allocated_banks; i++) {
> -                     digest_list[i].alg_id = chip->allocated_banks[i].alg_id;
> -                     memcpy(digest_list[i].digest, hash, TPM_DIGEST_SIZE);
> -             }
> +     for (i = 0; i < chip->nr_allocated_banks; i++)
> +             if (digests[i].alg_id != chip->allocated_banks[i].alg_id)
> +                     return -EINVAL;
>  
> -             rc = tpm2_pcr_extend(chip, pcr_idx, chip->nr_allocated_banks,
> -                                  digest_list);
> -             kfree(digest_list);
> +     if (chip->flags & TPM_CHIP_FLAG_TPM2) {
> +             rc = tpm2_pcr_extend(chip, pcr_idx, digests);
>               tpm_put_ops(chip);
>               return rc;
>       }
>  
> -     rc = tpm1_pcr_extend(chip, pcr_idx, hash,
> +     rc = tpm1_pcr_extend(chip, pcr_idx, digests[0].digest,
>                            "attempting extend a PCR value");
>       tpm_put_ops(chip);
>       return rc;
> diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
> index f5f15365b85b..5fb3f8bb279b 100644
> --- a/drivers/char/tpm/tpm.h
> +++ b/drivers/char/tpm/tpm.h
> @@ -453,7 +453,7 @@ static inline u32 tpm2_rc_value(u32 rc)
>  int tpm2_get_timeouts(struct tpm_chip *chip);
>  int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
>                 struct tpm_digest *digest, u16 *digest_size_ptr);
> -int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, u32 count,
> +int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
>                   struct tpm_digest *digests);
>  int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max);
>  void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle,
> diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
> index 760893957f13..dc115096e280 100644
> --- a/drivers/char/tpm/tpm2-cmd.c
> +++ b/drivers/char/tpm/tpm2-cmd.c
> @@ -247,12 +247,11 @@ struct tpm2_null_auth_area {
>   *
>   * @chip:    TPM chip to use.
>   * @pcr_idx: index of the PCR.
> - * @count:   number of digests passed.
>   * @digests: list of pcr banks and corresponding digest values to extend.
>   *
>   * Return: Same as with tpm_transmit_cmd.
>   */
> -int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, u32 count,
> +int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
>                   struct tpm_digest *digests)
>  {
>       struct tpm_buf buf;
> @@ -260,9 +259,6 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, 
> u32 count,
>       int rc;
>       int i;
>  
> -     if (count > chip->nr_allocated_banks)
> -             return -EINVAL;
> -
>       rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND);
>       if (rc)
>               return rc;
> @@ -277,9 +273,9 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, 
> u32 count,
>       tpm_buf_append_u32(&buf, sizeof(struct tpm2_null_auth_area));
>       tpm_buf_append(&buf, (const unsigned char *)&auth_area,
>                      sizeof(auth_area));
> -     tpm_buf_append_u32(&buf, count);
> +     tpm_buf_append_u32(&buf, chip->nr_allocated_banks);
>  
> -     for (i = 0; i < count; i++) {
> +     for (i = 0; i < chip->nr_allocated_banks; i++) {
>               tpm_buf_append_u16(&buf, digests[i].alg_id);
>               tpm_buf_append(&buf, (const unsigned char *)&digests[i].digest,
>                              chip->allocated_banks[i].digest_size);
> diff --git a/include/linux/tpm.h b/include/linux/tpm.h
> index 5ad0e58d5e09..08bf38e4c7aa 100644
> --- a/include/linux/tpm.h
> +++ b/include/linux/tpm.h
> @@ -175,7 +175,8 @@ struct tpm_chip {
>  extern int tpm_is_tpm2(struct tpm_chip *chip);
>  extern int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
>                       struct tpm_digest *digest);
> -extern int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 
> *hash);
> +extern int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
> +                       struct tpm_digest *digests);
>  extern int tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen);
>  extern int tpm_get_random(struct tpm_chip *chip, u8 *data, size_t max);
>  extern int tpm_seal_trusted(struct tpm_chip *chip,
> @@ -198,7 +199,7 @@ static inline int tpm_pcr_read(struct tpm_chip *chip, int 
> pcr_idx,
>  }
>  
>  static inline int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
> -                              const u8 *hash)
> +                              struct tpm_digest *digests)
>  {
>       return -ENODEV;
>  }
> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> index cc12f3449a72..e6b2dcb0846a 100644
> --- a/security/integrity/ima/ima.h
> +++ b/security/integrity/ima/ima.h
> @@ -56,6 +56,7 @@ extern int ima_policy_flag;
>  extern int ima_hash_algo;
>  extern int ima_appraise;
>  extern struct tpm_chip *ima_tpm_chip;
> +extern struct tpm_digest *digests;
>  
>  /* IMA event related data */
>  struct ima_event_data {
> diff --git a/security/integrity/ima/ima_init.c 
> b/security/integrity/ima/ima_init.c
> index 6bb42a9c5e47..296a965b11ef 100644
> --- a/security/integrity/ima/ima_init.c
> +++ b/security/integrity/ima/ima_init.c
> @@ -27,6 +27,7 @@
>  /* name for boot aggregate entry */
>  static const char boot_aggregate_name[] = "boot_aggregate";
>  struct tpm_chip *ima_tpm_chip;
> +struct tpm_digest *digests;
>  
>  /* Add the boot aggregate to the IMA measurement list and extend
>   * the PCR register.
> @@ -104,6 +105,24 @@ void __init ima_load_x509(void)
>  }
>  #endif
>  
> +int __init ima_init_digests(void)
> +{
> +     int i;
> +
> +     if (!ima_tpm_chip)
> +             return 0;
> +
> +     digests = kcalloc(ima_tpm_chip->nr_allocated_banks, sizeof(*digests),
> +                       GFP_NOFS);
> +     if (!digests)
> +             return -ENOMEM;
> +
> +     for (i = 0; i < ima_tpm_chip->nr_allocated_banks; i++)
> +             digests[i].alg_id = ima_tpm_chip->allocated_banks[i].alg_id;
> +
> +     return 0;
> +}
> +
>  int __init ima_init(void)
>  {
>       int rc;
> @@ -125,6 +144,9 @@ int __init ima_init(void)
>  
>       ima_load_kexec_buffer();
>  
> +     rc = ima_init_digests();
> +     if (rc != 0)
> +             return rc;
>       rc = ima_add_boot_aggregate();  /* boot aggregate must be first entry */
>       if (rc != 0)
>               return rc;
> diff --git a/security/integrity/ima/ima_queue.c 
> b/security/integrity/ima/ima_queue.c
> index 0e41dc1df1d4..719e88ca58f6 100644
> --- a/security/integrity/ima/ima_queue.c
> +++ b/security/integrity/ima/ima_queue.c
> @@ -140,11 +140,15 @@ unsigned long ima_get_binary_runtime_size(void)
>  static int ima_pcr_extend(const u8 *hash, int pcr)
>  {
>       int result = 0;
> +     int i;
>  
>       if (!ima_tpm_chip)
>               return result;
>  
> -     result = tpm_pcr_extend(ima_tpm_chip, pcr, hash);
> +     for (i = 0; i < ima_tpm_chip->nr_allocated_banks; i++)
> +             memcpy(digests[i].digest, hash, TPM_DIGEST_SIZE);
> +
> +     result = tpm_pcr_extend(ima_tpm_chip, pcr, digests);
>       if (result != 0)
>               pr_err("Error Communicating to TPM chip, result: %d\n", result);
>       return result;
> diff --git a/security/keys/trusted.c b/security/keys/trusted.c
> index 1a20a9692fef..baba0abc7850 100644
> --- a/security/keys/trusted.c
> +++ b/security/keys/trusted.c
> @@ -35,6 +35,7 @@
>  static const char hmac_alg[] = "hmac(sha1)";
>  static const char hash_alg[] = "sha1";
>  static struct tpm_chip *chip;
> +static struct tpm_digest *digests;
>  
>  struct sdesc {
>       struct shash_desc shash;
> @@ -380,15 +381,10 @@ EXPORT_SYMBOL_GPL(trusted_tpm_send);
>   */
>  static int pcrlock(const int pcrnum)
>  {
> -     unsigned char hash[SHA1_DIGEST_SIZE];
> -     int ret;
> -
>       if (!capable(CAP_SYS_ADMIN))
>               return -EPERM;
> -     ret = tpm_get_random(chip, hash, SHA1_DIGEST_SIZE);
> -     if (ret != SHA1_DIGEST_SIZE)
> -             return ret;
> -     return tpm_pcr_extend(chip, pcrnum, hash) ? -EINVAL : 0;
> +
> +     return tpm_pcr_extend(chip, pcrnum, digests) ? -EINVAL : 0;
>  }
>  
>  /*
> @@ -1222,6 +1218,32 @@ static int __init trusted_shash_alloc(void)
>       return ret;
>  }
>  
> +static int __init init_digests(void)
> +{
> +     int ret;
> +     int i;
> +
> +     digests = kcalloc(chip->nr_allocated_banks, sizeof(*digests),
> +                       GFP_KERNEL);
> +     if (!digests)
> +             return -ENOMEM;
> +
> +     ret = tpm_get_random(chip, digests[0].digest,
> +                          sizeof(digests[0].digest));
> +     if (ret != sizeof(digests[0].digest))
> +             return 0;

You forgot to free digests i.e. you leak memory.

> +
> +     for (i = 0; i < chip->nr_allocated_banks; i++) {
> +             digests[i].alg_id = chip->allocated_banks[i].alg_id;
> +
> +             if (i)
> +                     memcpy(digests[i].digest, digests[0].digest,
> +                            sizeof(digests[0].digest));

This just looks oddball.

What I would suggest is to change this definition a bit when you
move it to include/linux/tpm.h:

struct tpm2_digest {
        u16 alg_id;
        u8 digest[SHA512_DIGEST_SIZE];
} __packed;

Create a new constant for clarity:

#define TPM_MAX_DIGEST_SIZE SHA512_DIGEST_SIZE

And use redefine the struct as:

struct tpm_digest {
        u16 alg_id
        u8 digest[TPM_MAX_DIGEST_SIZE];
} __packed;

This should have been already done when that struct was defined in
the first place.

Then in that function create a temporary variable and grab the
random number there:

u8 digest[TPM_MAX_DIGEST_SIZE];

/* ... */

ret = tpm_get_random(chip, digest, TPM_MAX_DIGEST_SIZE);
if (ret < 0)
        return ret;
if (ret < TPM_MAX_DIGEST_SIZE)
        return -EFAULT;

After this you can allocate the array for digests.

/Jarkko

Reply via email to