Merge tag 'tpmdd-sessions-next-6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-tpmdd

Pull more tpm updates from Jarkko Sakkinen:
 "This is targeted for tpm2-sessions updates.

  There's two bug fixes and two more cosmetic tweaks for HMAC protected
  sessions. They provide a baseine for further improvements to be
  implemented during the the course of the release cycle"

* tag 'tpmdd-sessions-next-6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-tpmdd:
  tpm2-sessions: Open code tpm_buf_append_hmac_session()
  tpm2-sessions: Remove 'attributes' parameter from tpm_buf_append_auth
  tpm2-sessions: Fix tpm2_read_public range checks
  tpm2-sessions: Fix out of range indexing in name_size
This commit is contained in:
Linus Torvalds
2025-12-05 20:36:28 -08:00
4 changed files with 204 additions and 116 deletions

View File

@@ -11,8 +11,11 @@
* used by the kernel internally.
*/
#include "linux/dev_printk.h"
#include "linux/tpm.h"
#include "tpm.h"
#include <crypto/hash_info.h>
#include <linux/unaligned.h>
static bool disable_pcr_integrity;
module_param(disable_pcr_integrity, bool, 0444);
@@ -199,11 +202,15 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
}
if (!disable_pcr_integrity) {
tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
rc = tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
if (rc) {
tpm_buf_destroy(&buf);
return rc;
}
tpm_buf_append_hmac_session(chip, &buf, 0, NULL, 0);
} else {
tpm_buf_append_handle(chip, &buf, pcr_idx);
tpm_buf_append_auth(chip, &buf, 0, NULL, 0);
tpm_buf_append_auth(chip, &buf, NULL, 0);
}
tpm_buf_append_u32(&buf, chip->nr_allocated_banks);
@@ -214,8 +221,14 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
chip->allocated_banks[i].digest_size);
}
if (!disable_pcr_integrity)
tpm_buf_fill_hmac_session(chip, &buf);
if (!disable_pcr_integrity) {
rc = tpm_buf_fill_hmac_session(chip, &buf);
if (rc) {
tpm_buf_destroy(&buf);
return rc;
}
}
rc = tpm_transmit_cmd(chip, &buf, 0, "attempting extend a PCR value");
if (!disable_pcr_integrity)
rc = tpm_buf_check_hmac_response(chip, &buf, rc);
@@ -269,11 +282,24 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
do {
tpm_buf_reset(&buf, TPM2_ST_SESSIONS, TPM2_CC_GET_RANDOM);
tpm_buf_append_hmac_session_opt(chip, &buf, TPM2_SA_ENCRYPT
| TPM2_SA_CONTINUE_SESSION,
NULL, 0);
if (tpm2_chip_auth(chip)) {
tpm_buf_append_hmac_session(chip, &buf,
TPM2_SA_ENCRYPT |
TPM2_SA_CONTINUE_SESSION,
NULL, 0);
} else {
offset = buf.handles * 4 + TPM_HEADER_SIZE;
head = (struct tpm_header *)buf.data;
if (tpm_buf_length(&buf) == offset)
head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
}
tpm_buf_append_u16(&buf, num_bytes);
tpm_buf_fill_hmac_session(chip, &buf);
err = tpm_buf_fill_hmac_session(chip, &buf);
if (err) {
tpm_buf_destroy(&buf);
return err;
}
err = tpm_transmit_cmd(chip, &buf,
offsetof(struct tpm2_get_random_out,
buffer),

View File

@@ -144,59 +144,80 @@ struct tpm2_auth {
/*
* Name Size based on TPM algorithm (assumes no hash bigger than 255)
*/
static u8 name_size(const u8 *name)
static int name_size(const u8 *name)
{
static u8 size_map[] = {
[TPM_ALG_SHA1] = SHA1_DIGEST_SIZE,
[TPM_ALG_SHA256] = SHA256_DIGEST_SIZE,
[TPM_ALG_SHA384] = SHA384_DIGEST_SIZE,
[TPM_ALG_SHA512] = SHA512_DIGEST_SIZE,
};
u16 alg = get_unaligned_be16(name);
return size_map[alg] + 2;
u16 hash_alg = get_unaligned_be16(name);
switch (hash_alg) {
case TPM_ALG_SHA1:
return SHA1_DIGEST_SIZE + 2;
case TPM_ALG_SHA256:
return SHA256_DIGEST_SIZE + 2;
case TPM_ALG_SHA384:
return SHA384_DIGEST_SIZE + 2;
case TPM_ALG_SHA512:
return SHA512_DIGEST_SIZE + 2;
default:
pr_warn("tpm: unsupported name algorithm: 0x%04x\n", hash_alg);
return -EINVAL;
}
}
static int tpm2_parse_read_public(char *name, struct tpm_buf *buf)
static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
{
struct tpm_header *head = (struct tpm_header *)buf->data;
u32 mso = tpm2_handle_mso(handle);
off_t offset = TPM_HEADER_SIZE;
u32 tot_len = be32_to_cpu(head->length);
u32 val;
/* we're starting after the header so adjust the length */
tot_len -= TPM_HEADER_SIZE;
/* skip public */
val = tpm_buf_read_u16(buf, &offset);
if (val > tot_len)
return -EINVAL;
offset += val;
/* name */
val = tpm_buf_read_u16(buf, &offset);
if (val != name_size(&buf->data[offset]))
return -EINVAL;
memcpy(name, &buf->data[offset], val);
/* forget the rest */
return 0;
}
static int tpm2_read_public(struct tpm_chip *chip, u32 handle, char *name)
{
int rc, name_size_alg;
struct tpm_buf buf;
int rc;
if (mso != TPM2_MSO_PERSISTENT && mso != TPM2_MSO_VOLATILE &&
mso != TPM2_MSO_NVRAM) {
memcpy(name, &handle, sizeof(u32));
return sizeof(u32);
}
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_READ_PUBLIC);
if (rc)
return rc;
tpm_buf_append_u32(&buf, handle);
rc = tpm_transmit_cmd(chip, &buf, 0, "read public");
if (rc == TPM2_RC_SUCCESS)
rc = tpm2_parse_read_public(name, &buf);
tpm_buf_destroy(&buf);
rc = tpm_transmit_cmd(chip, &buf, 0, "TPM2_ReadPublic");
if (rc) {
tpm_buf_destroy(&buf);
return tpm_ret_to_err(rc);
}
return rc;
/* Skip TPMT_PUBLIC: */
offset += tpm_buf_read_u16(&buf, &offset);
/*
* Ensure space for the length field of TPM2B_NAME and hashAlg field of
* TPMT_HA (the extra four bytes).
*/
if (offset + 4 > tpm_buf_length(&buf)) {
tpm_buf_destroy(&buf);
return -EIO;
}
rc = tpm_buf_read_u16(&buf, &offset);
name_size_alg = name_size(&buf.data[offset]);
if (name_size_alg < 0)
return name_size_alg;
if (rc != name_size_alg) {
tpm_buf_destroy(&buf);
return -EIO;
}
if (offset + rc > tpm_buf_length(&buf)) {
tpm_buf_destroy(&buf);
return -EIO;
}
memcpy(name, &buf.data[offset], rc);
return name_size_alg;
}
#endif /* CONFIG_TCG_TPM2_HMAC */
@@ -221,52 +242,76 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, char *name)
* As with most tpm_buf operations, success is assumed because failure
* will be caused by an incorrect programming model and indicated by a
* kernel message.
*
* Ends the authorization session on failure.
*/
void tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
u32 handle, u8 *name)
int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
u32 handle, u8 *name)
{
#ifdef CONFIG_TCG_TPM2_HMAC
enum tpm2_mso_type mso = tpm2_handle_mso(handle);
struct tpm2_auth *auth;
u16 name_size_alg;
int slot;
int ret;
#endif
if (!tpm2_chip_auth(chip)) {
tpm_buf_append_handle(chip, buf, handle);
return;
return 0;
}
#ifdef CONFIG_TCG_TPM2_HMAC
slot = (tpm_buf_length(buf) - TPM_HEADER_SIZE) / 4;
if (slot >= AUTH_MAX_NAMES) {
dev_err(&chip->dev, "TPM: too many handles\n");
return;
dev_err(&chip->dev, "too many handles\n");
ret = -EIO;
goto err;
}
auth = chip->auth;
WARN(auth->session != tpm_buf_length(buf),
"name added in wrong place\n");
if (auth->session != tpm_buf_length(buf)) {
dev_err(&chip->dev, "session state malformed");
ret = -EIO;
goto err;
}
tpm_buf_append_u32(buf, handle);
auth->session += 4;
if (mso == TPM2_MSO_PERSISTENT ||
mso == TPM2_MSO_VOLATILE ||
mso == TPM2_MSO_NVRAM) {
if (!name)
tpm2_read_public(chip, handle, auth->name[slot]);
if (!name) {
ret = tpm2_read_public(chip, handle, auth->name[slot]);
if (ret < 0)
goto err;
name_size_alg = ret;
}
} else {
if (name)
dev_err(&chip->dev, "TPM: Handle does not require name but one is specified\n");
if (name) {
dev_err(&chip->dev, "handle 0x%08x does not use a name\n",
handle);
ret = -EIO;
goto err;
}
}
auth->name_h[slot] = handle;
if (name)
memcpy(auth->name[slot], name, name_size(name));
memcpy(auth->name[slot], name, name_size_alg);
#endif
return 0;
#ifdef CONFIG_TCG_TPM2_HMAC
err:
tpm2_end_auth_session(chip);
return tpm_ret_to_err(ret);
#endif
}
EXPORT_SYMBOL_GPL(tpm_buf_append_name);
void tpm_buf_append_auth(struct tpm_chip *chip, struct tpm_buf *buf,
u8 attributes, u8 *passphrase, int passphrase_len)
u8 *passphrase, int passphrase_len)
{
/* offset tells us where the sessions area begins */
int offset = buf->handles * 4 + TPM_HEADER_SIZE;
@@ -327,8 +372,7 @@ void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
#endif
if (!tpm2_chip_auth(chip)) {
tpm_buf_append_auth(chip, buf, attributes, passphrase,
passphrase_len);
tpm_buf_append_auth(chip, buf, passphrase, passphrase_len);
return;
}
@@ -533,11 +577,9 @@ static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip,
* encryption key and encrypts the first parameter of the command
* buffer with it.
*
* As with most tpm_buf operations, success is assumed because failure
* will be caused by an incorrect programming model and indicated by a
* kernel message.
* Ends the authorization session on failure.
*/
void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
{
u32 cc, handles, val;
struct tpm2_auth *auth = chip->auth;
@@ -549,9 +591,12 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
u8 cphash[SHA256_DIGEST_SIZE];
struct sha256_ctx sctx;
struct hmac_sha256_ctx hctx;
int ret;
if (!auth)
return;
if (!auth) {
ret = -EIO;
goto err;
}
/* save the command code in BE format */
auth->ordinal = head->ordinal;
@@ -560,9 +605,11 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
i = tpm2_find_cc(chip, cc);
if (i < 0) {
dev_err(&chip->dev, "Command 0x%x not found in TPM\n", cc);
return;
dev_err(&chip->dev, "command 0x%08x not found\n", cc);
ret = -EIO;
goto err;
}
attrs = chip->cc_attrs_tbl[i];
handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);
@@ -576,9 +623,9 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
u32 handle = tpm_buf_read_u32(buf, &offset_s);
if (auth->name_h[i] != handle) {
dev_err(&chip->dev, "TPM: handle %d wrong for name\n",
i);
return;
dev_err(&chip->dev, "invalid handle 0x%08x\n", handle);
ret = -EIO;
goto err;
}
}
/* point offset_s to the start of the sessions */
@@ -609,12 +656,14 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
offset_s += len;
}
if (offset_s != offset_p) {
dev_err(&chip->dev, "TPM session length is incorrect\n");
return;
dev_err(&chip->dev, "session length is incorrect\n");
ret = -EIO;
goto err;
}
if (!hmac) {
dev_err(&chip->dev, "TPM could not find HMAC session\n");
return;
dev_err(&chip->dev, "could not find HMAC session\n");
ret = -EIO;
goto err;
}
/* encrypt before HMAC */
@@ -646,8 +695,11 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
if (mso == TPM2_MSO_PERSISTENT ||
mso == TPM2_MSO_VOLATILE ||
mso == TPM2_MSO_NVRAM) {
sha256_update(&sctx, auth->name[i],
name_size(auth->name[i]));
ret = name_size(auth->name[i]);
if (ret < 0)
goto err;
sha256_update(&sctx, auth->name[i], ret);
} else {
__be32 h = cpu_to_be32(auth->name_h[i]);
@@ -668,6 +720,11 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
hmac_sha256_update(&hctx, auth->tpm_nonce, sizeof(auth->tpm_nonce));
hmac_sha256_update(&hctx, &auth->attrs, 1);
hmac_sha256_final(&hctx, hmac);
return 0;
err:
tpm2_end_auth_session(chip);
return ret;
}
EXPORT_SYMBOL(tpm_buf_fill_hmac_session);

View File

@@ -529,41 +529,18 @@ static inline struct tpm2_auth *tpm2_chip_auth(struct tpm_chip *chip)
#endif
}
void tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
u32 handle, u8 *name);
int 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,
u8 attributes, u8 *passphrase,
int passphraselen);
void tpm_buf_append_auth(struct tpm_chip *chip, struct tpm_buf *buf,
u8 attributes, u8 *passphrase, int passphraselen);
static inline void tpm_buf_append_hmac_session_opt(struct tpm_chip *chip,
struct tpm_buf *buf,
u8 attributes,
u8 *passphrase,
int passphraselen)
{
struct tpm_header *head;
int offset;
if (tpm2_chip_auth(chip)) {
tpm_buf_append_hmac_session(chip, buf, attributes, passphrase, passphraselen);
} else {
offset = buf->handles * 4 + TPM_HEADER_SIZE;
head = (struct tpm_header *)buf->data;
/*
* If the only sessions are optional, the command tag must change to
* TPM2_ST_NO_SESSIONS.
*/
if (tpm_buf_length(buf) == offset)
head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
}
}
u8 *passphrase, int passphraselen);
#ifdef CONFIG_TCG_TPM2_HMAC
int tpm2_start_auth_session(struct tpm_chip *chip);
void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf);
int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf);
int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf,
int rc);
void tpm2_end_auth_session(struct tpm_chip *chip);
@@ -577,10 +554,13 @@ static inline int tpm2_start_auth_session(struct tpm_chip *chip)
static inline void tpm2_end_auth_session(struct tpm_chip *chip)
{
}
static inline void tpm_buf_fill_hmac_session(struct tpm_chip *chip,
struct tpm_buf *buf)
static inline int tpm_buf_fill_hmac_session(struct tpm_chip *chip,
struct tpm_buf *buf)
{
return 0;
}
static inline int tpm_buf_check_hmac_response(struct tpm_chip *chip,
struct tpm_buf *buf,
int rc)

View File

@@ -268,7 +268,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
goto out_put;
}
tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
if (rc)
goto out;
tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_DECRYPT,
options->keyauth, TPM_DIGEST_SIZE);
@@ -316,7 +319,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
goto out;
}
tpm_buf_fill_hmac_session(chip, &buf);
rc = tpm_buf_fill_hmac_session(chip, &buf);
if (rc)
goto out;
rc = tpm_transmit_cmd(chip, &buf, 4, "sealing data");
rc = tpm_buf_check_hmac_response(chip, &buf, rc);
if (rc)
@@ -427,7 +433,10 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
return rc;
}
tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
if (rc)
goto out;
tpm_buf_append_hmac_session(chip, &buf, 0, options->keyauth,
TPM_DIGEST_SIZE);
@@ -439,7 +448,10 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
goto out;
}
tpm_buf_fill_hmac_session(chip, &buf);
rc = tpm_buf_fill_hmac_session(chip, &buf);
if (rc)
goto out;
rc = tpm_transmit_cmd(chip, &buf, 4, "loading blob");
rc = tpm_buf_check_hmac_response(chip, &buf, rc);
if (!rc)
@@ -469,8 +481,10 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
struct trusted_key_options *options,
u32 blob_handle)
{
struct tpm_header *head;
struct tpm_buf buf;
u16 data_len;
int offset;
u8 *data;
int rc;
@@ -484,7 +498,9 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
return rc;
}
tpm_buf_append_name(chip, &buf, blob_handle, NULL);
rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
if (rc)
goto out;
if (!options->policyhandle) {
tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT,
@@ -505,11 +521,20 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
tpm2_buf_append_auth(&buf, options->policyhandle,
NULL /* nonce */, 0, 0,
options->blobauth, options->blobauth_len);
tpm_buf_append_hmac_session_opt(chip, &buf, TPM2_SA_ENCRYPT,
NULL, 0);
if (tpm2_chip_auth(chip)) {
tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT, NULL, 0);
} else {
offset = buf.handles * 4 + TPM_HEADER_SIZE;
head = (struct tpm_header *)buf.data;
if (tpm_buf_length(&buf) == offset)
head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
}
}
tpm_buf_fill_hmac_session(chip, &buf);
rc = tpm_buf_fill_hmac_session(chip, &buf);
if (rc)
goto out;
rc = tpm_transmit_cmd(chip, &buf, 6, "unsealing");
rc = tpm_buf_check_hmac_response(chip, &buf, rc);