Commit d5b2ee0f authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
parents e42b1a9a df745e25
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -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);
+3 −0
Original line number Diff line number Diff line
@@ -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.
+4 −2
Original line number Diff line number Diff line
@@ -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);
	}
+60 −40
Original line number Diff line number Diff line
@@ -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:
	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;
}