Addresses a significant boot-time delay issue:
 
 https://bugzilla.kernel.org/show_bug.cgi?id=219229
 
 BR, Jarkko
 -----BEGIN PGP SIGNATURE-----
 
 iIgEABYKADAWIQRE6pSOnaBC00OEHEIaerohdGur0gUCZyAhHxIcamFya2tvQGtl
 cm5lbC5vcmcACgkQGnq6IXRrq9IdzQEAw8uoorY2IHBLFFyvYPebfHAZq5rPci8x
 eu1606gzODsBAIddSR4tgzFfqm0JVeh5vBa85wIZ43rRbUcpscrgEN8B
 =J+AF
 -----END PGP SIGNATURE-----

Merge tag 'tpmdd-next-6.12-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-tpmdd

Pull tpm fix from Jarkko Sakkinen:
 "Address a significant boot-time delay issue"

Link: https://bugzilla.kernel.org/show_bug.cgi?id=219229

* tag 'tpmdd-next-6.12-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-tpmdd:
  tpm: Lazily flush the auth session
  tpm: Rollback tpm2_load_null()
  tpm: Return tpm2_sessions_init() when null key creation fails
This commit is contained in:
Linus Torvalds 2024-10-29 16:04:24 -10:00
commit d5b2ee0fe8
4 changed files with 77 additions and 42 deletions

View file

@ -674,6 +674,16 @@ EXPORT_SYMBOL_GPL(tpm_chip_register);
*/
void tpm_chip_unregister(struct tpm_chip *chip)
{
#ifdef CONFIG_TCG_TPM2_HMAC
int rc;
rc = tpm_try_get_ops(chip);
if (!rc) {
tpm2_end_auth_session(chip);
tpm_put_ops(chip);
}
#endif
tpm_del_legacy_sysfs(chip);
if (tpm_is_hwrng_enabled(chip))
hwrng_unregister(&chip->hwrng);

View file

@ -27,6 +27,9 @@ 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_end_auth_session(chip);
ret = tpm2_prepare_space(chip, space, buf, bufsiz);
/* If the command is not implemented by the TPM, synthesize a
* response with a TPM2_RC_COMMAND_CODE return for user-space.

View file

@ -379,10 +379,12 @@ int tpm_pm_suspend(struct device *dev)
rc = tpm_try_get_ops(chip);
if (!rc) {
if (chip->flags & TPM_CHIP_FLAG_TPM2)
if (chip->flags & TPM_CHIP_FLAG_TPM2) {
tpm2_end_auth_session(chip);
tpm2_shutdown(chip, TPM2_SU_STATE);
else
} else {
rc = tpm1_pm_suspend(chip, tpm_suspend_pcr);
}
tpm_put_ops(chip);
}

View file

@ -333,6 +333,9 @@ void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
}
#ifdef CONFIG_TCG_TPM2_HMAC
/* The first write to /dev/tpm{rm0} will flush the session. */
attributes |= TPM2_SA_CONTINUE_SESSION;
/*
* The Architecture Guide requires us to strip trailing zeros
* before computing the HMAC
@ -484,7 +487,8 @@ static void tpm2_KDFe(u8 z[EC_PT_SZ], const char *str, u8 *pt_u, u8 *pt_v,
sha256_final(&sctx, out);
}
static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip)
static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip,
struct tpm2_auth *auth)
{
struct crypto_kpp *kpp;
struct kpp_request *req;
@ -543,7 +547,7 @@ static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip)
sg_set_buf(&s[0], chip->null_ec_key_x, EC_PT_SZ);
sg_set_buf(&s[1], chip->null_ec_key_y, EC_PT_SZ);
kpp_request_set_input(req, s, EC_PT_SZ*2);
sg_init_one(d, chip->auth->salt, EC_PT_SZ);
sg_init_one(d, auth->salt, EC_PT_SZ);
kpp_request_set_output(req, d, EC_PT_SZ);
crypto_kpp_compute_shared_secret(req);
kpp_request_free(req);
@ -554,8 +558,7 @@ static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip)
* This works because KDFe fully consumes the secret before it
* writes the salt
*/
tpm2_KDFe(chip->auth->salt, "SECRET", x, chip->null_ec_key_x,
chip->auth->salt);
tpm2_KDFe(auth->salt, "SECRET", x, chip->null_ec_key_x, auth->salt);
out:
crypto_free_kpp(kpp);
@ -853,7 +856,9 @@ int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf,
if (rc)
/* manually close the session if it wasn't consumed */
tpm2_flush_context(chip, auth->handle);
memzero_explicit(auth, sizeof(*auth));
kfree_sensitive(auth);
chip->auth = NULL;
} else {
/* reset for next use */
auth->session = TPM_HEADER_SIZE;
@ -881,7 +886,8 @@ void tpm2_end_auth_session(struct tpm_chip *chip)
return;
tpm2_flush_context(chip, auth->handle);
memzero_explicit(auth, sizeof(*auth));
kfree_sensitive(auth);
chip->auth = NULL;
}
EXPORT_SYMBOL(tpm2_end_auth_session);
@ -915,33 +921,37 @@ static int tpm2_parse_start_auth_session(struct tpm2_auth *auth,
static int tpm2_load_null(struct tpm_chip *chip, u32 *null_key)
{
int rc;
unsigned int offset = 0; /* dummy offset for null seed context */
u8 name[SHA256_DIGEST_SIZE + 2];
u32 tmp_null_key;
int rc;
rc = tpm2_load_context(chip, chip->null_key_context, &offset,
null_key);
if (rc != -EINVAL)
return rc;
&tmp_null_key);
if (rc != -EINVAL) {
if (!rc)
*null_key = tmp_null_key;
goto err;
}
/* an integrity failure may mean the TPM has been reset */
dev_err(&chip->dev, "NULL key integrity failure!\n");
/* check the null name against what we know */
tpm2_create_primary(chip, TPM2_RH_NULL, NULL, name);
if (memcmp(name, chip->null_key_name, sizeof(name)) == 0)
/* name unchanged, assume transient integrity failure */
return rc;
/*
* Fatal TPM failure: the NULL seed has actually changed, so
* the TPM must have been illegally reset. All in-kernel TPM
* operations will fail because the NULL primary can't be
* loaded to salt the sessions, but disable the TPM anyway so
* userspace programmes can't be compromised by it.
*/
dev_err(&chip->dev, "NULL name has changed, disabling TPM due to interference\n");
/* Try to re-create null key, given the integrity failure: */
rc = tpm2_create_primary(chip, TPM2_RH_NULL, &tmp_null_key, name);
if (rc)
goto err;
/* Return null key if the name has not been changed: */
if (!memcmp(name, chip->null_key_name, sizeof(name))) {
*null_key = tmp_null_key;
return 0;
}
/* Deduce from the name change TPM interference: */
dev_err(&chip->dev, "null key integrity check failed\n");
tpm2_flush_context(chip, tmp_null_key);
chip->flags |= TPM_CHIP_FLAG_DISABLE;
return rc;
err:
return rc ? -ENODEV : 0;
}
/**
@ -958,16 +968,20 @@ static int tpm2_load_null(struct tpm_chip *chip, u32 *null_key)
*/
int tpm2_start_auth_session(struct tpm_chip *chip)
{
struct tpm2_auth *auth;
struct tpm_buf buf;
struct tpm2_auth *auth = chip->auth;
int rc;
u32 null_key;
int rc;
if (!auth) {
dev_warn_once(&chip->dev, "auth session is not active\n");
if (chip->auth) {
dev_warn_once(&chip->dev, "auth session is active\n");
return 0;
}
auth = kzalloc(sizeof(*auth), GFP_KERNEL);
if (!auth)
return -ENOMEM;
rc = tpm2_load_null(chip, &null_key);
if (rc)
goto out;
@ -988,7 +1002,7 @@ int tpm2_start_auth_session(struct tpm_chip *chip)
tpm_buf_append(&buf, auth->our_nonce, sizeof(auth->our_nonce));
/* append encrypted salt and squirrel away unencrypted in auth */
tpm_buf_append_salt(&buf, chip);
tpm_buf_append_salt(&buf, chip, auth);
/* session type (HMAC, audit or policy) */
tpm_buf_append_u8(&buf, TPM2_SE_HMAC);
@ -1010,10 +1024,13 @@ int tpm2_start_auth_session(struct tpm_chip *chip)
tpm_buf_destroy(&buf);
if (rc)
goto out;
if (rc == TPM2_RC_SUCCESS) {
chip->auth = auth;
return 0;
}
out:
out:
kfree_sensitive(auth);
return rc;
}
EXPORT_SYMBOL(tpm2_start_auth_session);
@ -1347,18 +1364,21 @@ static int tpm2_create_null_primary(struct tpm_chip *chip)
*
* Derive and context save the null primary and allocate memory in the
* struct tpm_chip for the authorizations.
*
* Return:
* * 0 - OK
* * -errno - A system error
* * TPM_RC - A TPM error
*/
int tpm2_sessions_init(struct tpm_chip *chip)
{
int rc;
rc = tpm2_create_null_primary(chip);
if (rc)
dev_err(&chip->dev, "TPM: security failed (NULL seed derivation): %d\n", rc);
chip->auth = kmalloc(sizeof(*chip->auth), GFP_KERNEL);
if (!chip->auth)
return -ENOMEM;
if (rc) {
dev_err(&chip->dev, "null key creation failed with %d\n", rc);
return rc;
}
return rc;
}