linux-integrity.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Jarkko Sakkinen <jarkko@kernel.org>
To: Matthew Garrett <matthewg@nvidia.com>
Cc: mjg59@srcf.ucam.org, keyrings@vger.kernel.org,
	James.Bottomley@hansenpartnership.com,
	linux-integrity@vger.kernel.org, rafael@kernel.org,
	linux-pm@vger.kernel.org, linux-efi@vger.kernel.org
Subject: Re: [RFC] Make hibernation work with lockdown
Date: Sun, 15 Nov 2026 17:08:53 +0200	[thread overview]
Message-ID: <avnLBQwPuF5AFQh2@kernel.org> (raw)
In-Reply-To: <avnJh0nsORR45i4m@kernel.org>

On Sun, Nov 15, 2026 at 05:02:35PM +0200, Jarkko Sakkinen wrote:
> Here's a patch that encapsulates my thoughts.
> 
> Br, Jarkko

> From c9a5b8d99278874bdd79d19bddd1d4a56ad8a38a Mon Sep 17 00:00:00 2001
> From: Jarkko Sakkinen <jarkko@kernel.org>
> Date: Sun, 15 Nov 2026 17:00:47 +0200
> Subject: [PATCH] tpm: Deny access to the hibernate NV index
> 
> 1. Add the TPM2 command codes mutating the reserved NV index, and describe
>    their target handles.
> 2. Replace tpm2_ordinal_duration_map with tpm2_cc_map providing handle
>    indices for the commands mutating NV indices.
> 3. Add tpm2_is_user_command() for identifying commands allowed from user
>    space. It will block commands trying to mutate the reserve NV index.
> 
> NV index should be protected regardless whether hibernate is used or not
> because not doing that introduces unwanted asymmetry.
> 
> Co-developed-by: Matthew Garrett <matthewg@nvidia.com>
> Signed-off-by: Matthew Garrett <matthewg@nvidia.com>
> Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
> ---
>  drivers/char/tpm/tpm-dev-common.c |  4 ++
>  drivers/char/tpm/tpm.h            |  1 +
>  drivers/char/tpm/tpm2-cmd.c       | 96 +++++++++++++++++++++++--------
>  include/linux/tpm.h               |  6 ++
>  include/linux/tpm_command.h       |  9 +++
>  5 files changed, 91 insertions(+), 25 deletions(-)
> 
> diff --git a/drivers/char/tpm/tpm-dev-common.c b/drivers/char/tpm/tpm-dev-common.c
> index ee91f456f975..4394b8f59012 100644
> --- a/drivers/char/tpm/tpm-dev-common.c
> +++ b/drivers/char/tpm/tpm-dev-common.c
> @@ -27,6 +27,10 @@ static ssize_t tpm_dev_transmit(struct tpm_chip *chip, struct tpm_space *space,
>  	struct tpm_header *header = (void *)buf;
>  	ssize_t ret, len;
>  
> +	if ((chip->flags & TPM_CHIP_FLAG_TPM2) &&
> +	    !tpm2_is_user_command(buf, bufsiz))
> +		return -EPERM;
> +
>  	if (chip->flags & TPM_CHIP_FLAG_TPM2)
>  		tpm2_end_auth_session(chip);
>  
> diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
> index fa554c5ad80b..2b18acaae920 100644
> --- a/drivers/char/tpm/tpm.h
> +++ b/drivers/char/tpm/tpm.h
> @@ -107,6 +107,7 @@ static inline void tpm_add_ppi(struct tpm_chip *chip)
>  }
>  #endif
>  
> +bool tpm2_is_user_command(const u8 *buf, size_t bufsiz);
>  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);
> diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
> index 4b2bb2ddc321..50c7ada9a4d6 100644
> --- a/drivers/char/tpm/tpm2-cmd.c
> +++ b/drivers/char/tpm/tpm2-cmd.c
> @@ -51,29 +51,43 @@ int tpm2_get_timeouts(struct tpm_chip *chip)
>  	return 0;
>  }
>  
> -/*
> - * Contains the maximum durations in milliseconds for TPM2 commands.
> - */
> +enum tpm2_cc_nv {
> +	TPM2_CC_NV_NA = -1, /* not available */
> +	TPM2_CC_NV_H0 = 0,
> +	TPM2_CC_NV_H1 = 1,
> +};
> +
> +/* Command durations in milliseconds and NV index handle positions. */
>  static const struct {
> -	unsigned long ordinal;
> -	unsigned long duration;
> -} tpm2_ordinal_duration_map[] = {
> -	{TPM2_CC_STARTUP, 750},
> -	{TPM2_CC_SELF_TEST, 3000},
> -	{TPM2_CC_GET_RANDOM, 2000},
> -	{TPM2_CC_SEQUENCE_UPDATE, 750},
> -	{TPM2_CC_SEQUENCE_COMPLETE, 750},
> -	{TPM2_CC_EVENT_SEQUENCE_COMPLETE, 750},
> -	{TPM2_CC_HASH_SEQUENCE_START, 750},
> -	{TPM2_CC_VERIFY_SIGNATURE, 30000},
> -	{TPM2_CC_PCR_EXTEND, 750},
> -	{TPM2_CC_HIERARCHY_CONTROL, 2000},
> -	{TPM2_CC_HIERARCHY_CHANGE_AUTH, 2000},
> -	{TPM2_CC_GET_CAPABILITY, 750},
> -	{TPM2_CC_NV_READ, 2000},
> -	{TPM2_CC_CREATE_PRIMARY, 300000},
> -	{TPM2_CC_CREATE, 300000},
> -	{TPM2_CC_CREATE_LOADED, 300000},
> +	u32 cc;
> +	u32 duration;
> +	enum tpm2_cc_nv nv;
> +} tpm2_cc_map[] = {
> +	{TPM2_CC_STARTUP, 750, TPM2_CC_NV_NA},
> +	{TPM2_CC_SELF_TEST, 3000, TPM2_CC_NV_NA},
> +	{TPM2_CC_GET_RANDOM, 2000, TPM2_CC_NV_NA},
> +	{TPM2_CC_SEQUENCE_UPDATE, 750, TPM2_CC_NV_NA},
> +	{TPM2_CC_SEQUENCE_COMPLETE, 750, TPM2_CC_NV_NA},
> +	{TPM2_CC_EVENT_SEQUENCE_COMPLETE, 750, TPM2_CC_NV_NA},
> +	{TPM2_CC_HASH_SEQUENCE_START, 750, TPM2_CC_NV_NA},
> +	{TPM2_CC_VERIFY_SIGNATURE, 30000, TPM2_CC_NV_NA},
> +	{TPM2_CC_PCR_EXTEND, 750, TPM2_CC_NV_NA},
> +	{TPM2_CC_HIERARCHY_CONTROL, 2000, TPM2_CC_NV_NA},
> +	{TPM2_CC_HIERARCHY_CHANGE_AUTH, 2000, TPM2_CC_NV_NA},
> +	{TPM2_CC_GET_CAPABILITY, 750, TPM2_CC_NV_NA},
> +	{TPM2_CC_NV_READ, 2000, TPM2_CC_NV_NA},
> +	{TPM2_CC_CREATE_PRIMARY, 300000, TPM2_CC_NV_NA},
> +	{TPM2_CC_CREATE, 300000, TPM2_CC_NV_NA},
> +	{TPM2_CC_CREATE_LOADED, 300000, TPM2_CC_NV_NA},
> +	{TPM2_CC_NV_UNDEFINE_SPACE_SPECIAL, TPM2_DURATION_DEFAULT, TPM2_CC_NV_H0},
> +	{TPM2_CC_NV_UNDEFINE_SPACE, TPM2_DURATION_DEFAULT, TPM2_CC_NV_H1},
> +	{TPM2_CC_NV_INCREMENT, TPM2_DURATION_DEFAULT, TPM2_CC_NV_H1},
> +	{TPM2_CC_NV_SET_BITS, TPM2_DURATION_DEFAULT, TPM2_CC_NV_H1},
> +	{TPM2_CC_NV_EXTEND, TPM2_DURATION_DEFAULT, TPM2_CC_NV_H1},
> +	{TPM2_CC_NV_WRITE, TPM2_DURATION_DEFAULT, TPM2_CC_NV_H1},
> +	{TPM2_CC_NV_WRITE_LOCK, TPM2_DURATION_DEFAULT, TPM2_CC_NV_H1},
> +	{TPM2_CC_NV_CHANGE_AUTH, TPM2_DURATION_DEFAULT, TPM2_CC_NV_H0},
> +	{TPM2_CC_NV_READ_LOCK, TPM2_DURATION_DEFAULT, TPM2_CC_NV_H1},
>  };
>  
>  /**
> @@ -87,13 +101,45 @@ unsigned long tpm2_calc_ordinal_duration(u32 ordinal)
>  {
>  	int i;
>  
> -	for (i = 0; i < ARRAY_SIZE(tpm2_ordinal_duration_map); i++)
> -		if (ordinal == tpm2_ordinal_duration_map[i].ordinal)
> -			return msecs_to_jiffies(tpm2_ordinal_duration_map[i].duration);
> +	for (i = 0; i < ARRAY_SIZE(tpm2_cc_map); i++)
> +		if (ordinal == tpm2_cc_map[i].cc)
> +			return msecs_to_jiffies(tpm2_cc_map[i].duration);
>  
>  	return msecs_to_jiffies(TPM2_DURATION_DEFAULT);
>  }
>  
> +/* Check whether a command is permitted through the userspace interface. */
> +bool tpm2_is_user_command(const u8 *buf, size_t bufsiz)
> +{
> +	const struct tpm_header *header = (const void *)buf;
> +	size_t len, offset;
> +	u32 cc, index;
> +	int i;
> +
> +	if (bufsiz < TPM_HEADER_SIZE)
> +		return true;
> +
> +	len = min_t(size_t, be32_to_cpu(header->length), bufsiz);
> +	if (len < TPM_HEADER_SIZE)
> +		return true;
> +
> +	cc = be32_to_cpu(header->ordinal);
> +
> +	for (i = 0; i < ARRAY_SIZE(tpm2_cc_map); i++) {
> +		if (tpm2_cc_map[i].cc != cc || tpm2_cc_map[i].nv == TPM2_CC_NV_NA)
> +			continue;
> +
> +		offset = TPM_HEADER_SIZE + tpm2_cc_map[i].nv * sizeof(u32);
> +		if (len < offset + sizeof(u32))
> +			return true;
> +
> +		index = get_unaligned_be32(&buf[offset]);
> +		return index != TPM2_KERNEL_NV_INDEX;
> +	}
> +
> +	return true;
> +}
> +
>  /**
>   * tpm2_pcr_read() - read a PCR value
>   * @chip:	TPM chip to use.
> diff --git a/include/linux/tpm.h b/include/linux/tpm.h
> index 0db277af45c3..2a114c0ffcf9 100644
> --- a/include/linux/tpm.h
> +++ b/include/linux/tpm.h
> @@ -178,6 +178,12 @@ static inline enum tpm2_mso_type tpm2_handle_mso(u32 handle)
>  	return handle >> 24;
>  }
>  
> +/*
> + * Provisional kernel-owned NV index. Userspace is not permitted to undefine
> + * or modify it. The allocation has not yet been finalized.
> + */
> +#define TPM2_KERNEL_NV_INDEX	0x014c4853
> +
>  #define TPM_VID_INTEL    0x8086
>  #define TPM_VID_WINBOND  0x1050
>  #define TPM_VID_STM      0x104A
> diff --git a/include/linux/tpm_command.h b/include/linux/tpm_command.h
> index 9ea1ee3cbef1..9224e81cb8f2 100644
> --- a/include/linux/tpm_command.h
> +++ b/include/linux/tpm_command.h
> @@ -208,14 +208,23 @@ enum tpm2_return_codes {
>  
>  enum tpm2_command_codes {
>  	TPM2_CC_FIRST			= 0x011F,
> +	TPM2_CC_NV_UNDEFINE_SPACE_SPECIAL = 0x011F,
>  	TPM2_CC_HIERARCHY_CONTROL	= 0x0121,
> +	TPM2_CC_NV_UNDEFINE_SPACE	= 0x0122,
>  	TPM2_CC_HIERARCHY_CHANGE_AUTH	= 0x0129,
>  	TPM2_CC_CREATE_PRIMARY		= 0x0131,
> +	TPM2_CC_NV_INCREMENT		= 0x0134,
> +	TPM2_CC_NV_SET_BITS		= 0x0135,
> +	TPM2_CC_NV_EXTEND		= 0x0136,
> +	TPM2_CC_NV_WRITE			= 0x0137,
> +	TPM2_CC_NV_WRITE_LOCK		= 0x0138,
> +	TPM2_CC_NV_CHANGE_AUTH		= 0x013B,
>  	TPM2_CC_SEQUENCE_COMPLETE	= 0x013E,
>  	TPM2_CC_SELF_TEST		= 0x0143,
>  	TPM2_CC_STARTUP			= 0x0144,
>  	TPM2_CC_SHUTDOWN		= 0x0145,
>  	TPM2_CC_NV_READ			= 0x014E,
> +	TPM2_CC_NV_READ_LOCK		= 0x014F,
>  	TPM2_CC_CREATE			= 0x0153,
>  	TPM2_CC_LOAD			= 0x0157,
>  	TPM2_CC_SEQUENCE_UPDATE		= 0x015C,
> -- 
> 2.47.3
> 

The additional benefit of this: we can create a kselftest if we want.

Br, Jarkko

      reply	other threads:[~2026-11-15 15:08 UTC|newest]

Thread overview: 58+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-08 13:20 [RFC] Make hibernation work with lockdown Matthew Garrett
2026-10-08 13:20 ` [PATCH 01/17] tpm: Define a kernel-owned TPM NV index that can't be modified by userland Matthew Garrett
2026-10-08 13:41   ` Matthew Garrett
2026-10-08 16:24     ` Jarkko Sakkinen
2026-10-08 16:23   ` Jarkko Sakkinen
2026-10-09  8:33     ` Matthew Garrett
2026-10-10 21:36       ` Jarkko Sakkinen
2026-10-08 17:06   ` Ilias Apalodimas
2026-10-10  9:22   ` James Bottomley
2026-10-10 21:26     ` Matthew Garrett
2026-10-11  6:04       ` James Bottomley
2026-10-11 10:23         ` Matthew Garrett
2026-10-11 10:27           ` James Bottomley
2026-10-11 11:28             ` Matthew Garrett
2026-11-15 15:15               ` Jarkko Sakkinen
2026-10-11  8:21       ` James Bottomley
2026-11-13  0:40   ` Jarkko Sakkinen
2026-11-13  0:45     ` Jarkko Sakkinen
2026-11-13  0:50       ` Jarkko Sakkinen
2026-10-08 13:20 ` [PATCH 02/17] efi: Add a mechanism to modify TPM state depending on kernel security features Matthew Garrett
2026-10-08 16:38   ` Jarkko Sakkinen
2026-10-08 13:20 ` [PATCH 03/17] tpm: Allow tpm2_start_auth_session() to start an audit session Matthew Garrett
2026-11-13  2:17   ` Jarkko Sakkinen
2026-10-08 13:20 ` [PATCH 04/17] tpm: Log commands executed in " Matthew Garrett
2026-10-08 13:20 ` [PATCH 05/17] tpm: Add a kernel attestation key and signed audit digest retrieval Matthew Garrett
2026-10-08 16:45   ` James Bottomley
2026-10-09  8:29     ` Matthew Garrett
2026-10-08 13:20 ` [PATCH 06/17] tpm: Use TPM2_NV_ReadPublic to read NV index names Matthew Garrett
2026-10-08 13:20 ` [PATCH 07/17] tpm: Add in-kernel support for reading NV indices Matthew Garrett
2026-10-08 13:20 ` [PATCH 08/17] tpm: Add NV define, undefine and write helpers Matthew Garrett
2026-10-08 13:20 ` [PATCH 09/17] tpm: Provision the kernel NV index at registration Matthew Garrett
2026-10-08 13:20 ` [PATCH 10/17] tpm: Move the bounds-checked response reader to a header Matthew Garrett
2026-10-08 13:20 ` [PATCH 11/17] tpm: Add kernel signing key creation with audited provenance Matthew Garrett
2026-10-08 17:00   ` James Bottomley
2026-10-09  8:31     ` Matthew Garrett
2026-10-08 13:20 ` [PATCH 12/17] tpm: Add signing with the kernel signing key Matthew Garrett
2026-10-08 13:20 ` [PATCH 13/17] tpm: Add verification of kernel signing key provenance Matthew Garrett
2026-10-10  9:34   ` James Bottomley
2026-10-10 21:18     ` Matthew Garrett
2026-10-10  9:42   ` James Bottomley
2026-10-10 21:20     ` Matthew Garrett
2026-11-13  2:25   ` Jarkko Sakkinen
2026-10-08 13:20 ` [PATCH 14/17] PM: hibernate: Add image digest and signature page infrastructure Matthew Garrett
2026-10-08 13:20 ` [PATCH 15/17] PM: hibernate: Sign and verify images with a kernel-generated TPM key Matthew Garrett
2026-10-08 13:20 ` [PATCH 16/17] PM: hibernate: Refuse to verify images with a virtual TPM Matthew Garrett
2026-10-08 13:20 ` [PATCH 17/17] PM: hibernate: Allow hibernation under lockdown with signed images Matthew Garrett
2026-10-08 16:53   ` Jarkko Sakkinen
2026-10-09  8:31     ` Matthew Garrett
2026-10-10 21:29       ` Jarkko Sakkinen
2026-10-08 15:56 ` [RFC] Make hibernation work with lockdown Jarkko Sakkinen
2026-10-23 15:28 ` Jarkko Sakkinen
2026-10-23 19:13   ` Jarkko Sakkinen
2026-10-31  3:01     ` Jarkko Sakkinen
2026-11-07 22:34 ` Neal Gompa
2026-11-10  1:36 ` Jarkko Sakkinen
2026-11-13  0:06   ` Jarkko Sakkinen
2026-11-15 15:02 ` Jarkko Sakkinen
2026-11-15 15:08   ` Jarkko Sakkinen [this message]

Reply instructions:

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

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

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

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

  git send-email \
    --in-reply-to=avnLBQwPuF5AFQh2@kernel.org \
    --to=jarkko@kernel.org \
    --cc=James.Bottomley@hansenpartnership.com \
    --cc=keyrings@vger.kernel.org \
    --cc=linux-efi@vger.kernel.org \
    --cc=linux-integrity@vger.kernel.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=matthewg@nvidia.com \
    --cc=mjg59@srcf.ucam.org \
    --cc=rafael@kernel.org \
    /path/to/YOUR_REPLY

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

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is 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).