Commit 7ca110f2 authored by Jarkko Sakkinen's avatar Jarkko Sakkinen Committed by Jarkko Sakkinen
Browse files

tpm: Address !chip->auth in tpm_buf_append_hmac_session*()



Unless tpm_chip_bootstrap() was called by the driver, !chip->auth can
cause a null derefence in tpm_buf_hmac_session*().  Thus, address
!chip->auth in tpm_buf_hmac_session*() and remove the fallback
implementation for !TCG_TPM2_HMAC.

Cc: stable@vger.kernel.org # v6.9+
Reported-by: default avatarStefan Berger <stefanb@linux.ibm.com>
Closes: https://lore.kernel.org/linux-integrity/20240617193408.1234365-1-stefanb@linux.ibm.com/


Fixes: 1085b827 ("tpm: Add the rest of the session HMAC API")
Tested-by: Michael Ellerman <mpe@ellerman.id.au> # ppc
Signed-off-by: default avatarJarkko Sakkinen <jarkko@kernel.org>
parent a61809a3
Loading
Loading
Loading
Loading
+110 −76
Original line number Diff line number Diff line
@@ -272,6 +272,110 @@ void tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
}
EXPORT_SYMBOL_GPL(tpm_buf_append_name);

/**
 * tpm_buf_append_hmac_session() - Append a TPM session element
 * @chip: the TPM chip structure
 * @buf: The buffer to be appended
 * @attributes: The session attributes
 * @passphrase: The session authority (NULL if none)
 * @passphrase_len: The length of the session authority (0 if none)
 *
 * This fills in a session structure in the TPM command buffer, except
 * for the HMAC which cannot be computed until the command buffer is
 * complete.  The type of session is controlled by the @attributes,
 * the main ones of which are TPM2_SA_CONTINUE_SESSION which means the
 * session won't terminate after tpm_buf_check_hmac_response(),
 * TPM2_SA_DECRYPT which means this buffers first parameter should be
 * encrypted with a session key and TPM2_SA_ENCRYPT, which means the
 * response buffer's first parameter needs to be decrypted (confusing,
 * but the defines are written from the point of view of the TPM).
 *
 * Any session appended by this command must be finalized by calling
 * tpm_buf_fill_hmac_session() otherwise the HMAC will be incorrect
 * and the TPM will reject the command.
 *
 * 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.
 */
void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
				 u8 attributes, u8 *passphrase,
				 int passphrase_len)
{
#ifdef CONFIG_TCG_TPM2_HMAC
	u8 nonce[SHA256_DIGEST_SIZE];
	struct tpm2_auth *auth;
	u32 len;
#endif

	if (!tpm2_chip_auth(chip)) {
		/* offset tells us where the sessions area begins */
		int offset = buf->handles * 4 + TPM_HEADER_SIZE;
		u32 len = 9 + passphrase_len;

		if (tpm_buf_length(buf) != offset) {
			/* not the first session so update the existing length */
			len += get_unaligned_be32(&buf->data[offset]);
			put_unaligned_be32(len, &buf->data[offset]);
		} else {
			tpm_buf_append_u32(buf, len);
		}
		/* auth handle */
		tpm_buf_append_u32(buf, TPM2_RS_PW);
		/* nonce */
		tpm_buf_append_u16(buf, 0);
		/* attributes */
		tpm_buf_append_u8(buf, 0);
		/* passphrase */
		tpm_buf_append_u16(buf, passphrase_len);
		tpm_buf_append(buf, passphrase, passphrase_len);
		return;
	}

#ifdef CONFIG_TCG_TPM2_HMAC
	/*
	 * The Architecture Guide requires us to strip trailing zeros
	 * before computing the HMAC
	 */
	while (passphrase && passphrase_len > 0 && passphrase[passphrase_len - 1] == '\0')
		passphrase_len--;

	auth = chip->auth;
	auth->attrs = attributes;
	auth->passphrase_len = passphrase_len;
	if (passphrase_len)
		memcpy(auth->passphrase, passphrase, passphrase_len);

	if (auth->session != tpm_buf_length(buf)) {
		/* we're not the first session */
		len = get_unaligned_be32(&buf->data[auth->session]);
		if (4 + len + auth->session != tpm_buf_length(buf)) {
			WARN(1, "session length mismatch, cannot append");
			return;
		}

		/* add our new session */
		len += 9 + 2 * SHA256_DIGEST_SIZE;
		put_unaligned_be32(len, &buf->data[auth->session]);
	} else {
		tpm_buf_append_u32(buf, 9 + 2 * SHA256_DIGEST_SIZE);
	}

	/* random number for our nonce */
	get_random_bytes(nonce, sizeof(nonce));
	memcpy(auth->our_nonce, nonce, sizeof(nonce));
	tpm_buf_append_u32(buf, auth->handle);
	/* our new nonce */
	tpm_buf_append_u16(buf, SHA256_DIGEST_SIZE);
	tpm_buf_append(buf, nonce, SHA256_DIGEST_SIZE);
	tpm_buf_append_u8(buf, auth->attrs);
	/* and put a placeholder for the hmac */
	tpm_buf_append_u16(buf, SHA256_DIGEST_SIZE);
	tpm_buf_append(buf, nonce, SHA256_DIGEST_SIZE);
#endif
}
EXPORT_SYMBOL_GPL(tpm_buf_append_hmac_session);

#ifdef CONFIG_TCG_TPM2_HMAC

static int tpm2_create_primary(struct tpm_chip *chip, u32 hierarchy,
@@ -457,82 +561,6 @@ static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip)
	crypto_free_kpp(kpp);
}

/**
 * tpm_buf_append_hmac_session() - Append a TPM session element
 * @chip: the TPM chip structure
 * @buf: The buffer to be appended
 * @attributes: The session attributes
 * @passphrase: The session authority (NULL if none)
 * @passphrase_len: The length of the session authority (0 if none)
 *
 * This fills in a session structure in the TPM command buffer, except
 * for the HMAC which cannot be computed until the command buffer is
 * complete.  The type of session is controlled by the @attributes,
 * the main ones of which are TPM2_SA_CONTINUE_SESSION which means the
 * session won't terminate after tpm_buf_check_hmac_response(),
 * TPM2_SA_DECRYPT which means this buffers first parameter should be
 * encrypted with a session key and TPM2_SA_ENCRYPT, which means the
 * response buffer's first parameter needs to be decrypted (confusing,
 * but the defines are written from the point of view of the TPM).
 *
 * Any session appended by this command must be finalized by calling
 * tpm_buf_fill_hmac_session() otherwise the HMAC will be incorrect
 * and the TPM will reject the command.
 *
 * 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.
 */
void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
				 u8 attributes, u8 *passphrase,
				 int passphrase_len)
{
	u8 nonce[SHA256_DIGEST_SIZE];
	u32 len;
	struct tpm2_auth *auth = chip->auth;

	/*
	 * The Architecture Guide requires us to strip trailing zeros
	 * before computing the HMAC
	 */
	while (passphrase && passphrase_len > 0
	       && passphrase[passphrase_len - 1] == '\0')
		passphrase_len--;

	auth->attrs = attributes;
	auth->passphrase_len = passphrase_len;
	if (passphrase_len)
		memcpy(auth->passphrase, passphrase, passphrase_len);

	if (auth->session != tpm_buf_length(buf)) {
		/* we're not the first session */
		len = get_unaligned_be32(&buf->data[auth->session]);
		if (4 + len + auth->session != tpm_buf_length(buf)) {
			WARN(1, "session length mismatch, cannot append");
			return;
		}

		/* add our new session */
		len += 9 + 2 * SHA256_DIGEST_SIZE;
		put_unaligned_be32(len, &buf->data[auth->session]);
	} else {
		tpm_buf_append_u32(buf, 9 + 2 * SHA256_DIGEST_SIZE);
	}

	/* random number for our nonce */
	get_random_bytes(nonce, sizeof(nonce));
	memcpy(auth->our_nonce, nonce, sizeof(nonce));
	tpm_buf_append_u32(buf, auth->handle);
	/* our new nonce */
	tpm_buf_append_u16(buf, SHA256_DIGEST_SIZE);
	tpm_buf_append(buf, nonce, SHA256_DIGEST_SIZE);
	tpm_buf_append_u8(buf, auth->attrs);
	/* and put a placeholder for the hmac */
	tpm_buf_append_u16(buf, SHA256_DIGEST_SIZE);
	tpm_buf_append(buf, nonce, SHA256_DIGEST_SIZE);
}
EXPORT_SYMBOL(tpm_buf_append_hmac_session);

/**
 * tpm_buf_fill_hmac_session() - finalize the session HMAC
 * @chip: the TPM chip structure
@@ -563,6 +591,9 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
	u8 cphash[SHA256_DIGEST_SIZE];
	struct sha256_state sctx;

	if (!auth)
		return;

	/* save the command code in BE format */
	auth->ordinal = head->ordinal;

@@ -721,6 +752,9 @@ int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf,
	u32 cc = be32_to_cpu(auth->ordinal);
	int parm_len, len, i, handles;

	if (!auth)
		return rc;

	if (auth->session >= TPM_HEADER_SIZE) {
		WARN(1, "tpm session not filled correctly\n");
		goto out;
+20 −48
Original line number Diff line number Diff line
@@ -502,10 +502,6 @@ static inline struct tpm2_auth *tpm2_chip_auth(struct tpm_chip *chip)

void tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
			 u32 handle, u8 *name);

#ifdef CONFIG_TCG_TPM2_HMAC

int tpm2_start_auth_session(struct tpm_chip *chip);
void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
				 u8 attributes, u8 *passphrase,
				 int passphraselen);
@@ -515,9 +511,27 @@ static inline void tpm_buf_append_hmac_session_opt(struct tpm_chip *chip,
						   u8 *passphrase,
						   int passphraselen)
{
	tpm_buf_append_hmac_session(chip, buf, attributes, passphrase,
				    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);
	}
}

#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_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf,
				int rc);
@@ -532,48 +546,6 @@ 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_append_hmac_session(struct tpm_chip *chip,
					       struct tpm_buf *buf,
					       u8 attributes, u8 *passphrase,
					       int passphraselen)
{
	/* offset tells us where the sessions area begins */
	int offset = buf->handles * 4 + TPM_HEADER_SIZE;
	u32 len = 9 + passphraselen;

	if (tpm_buf_length(buf) != offset) {
		/* not the first session so update the existing length */
		len += get_unaligned_be32(&buf->data[offset]);
		put_unaligned_be32(len, &buf->data[offset]);
	} else {
		tpm_buf_append_u32(buf, len);
	}
	/* auth handle */
	tpm_buf_append_u32(buf, TPM2_RS_PW);
	/* nonce */
	tpm_buf_append_u16(buf, 0);
	/* attributes */
	tpm_buf_append_u8(buf, 0);
	/* passphrase */
	tpm_buf_append_u16(buf, passphraselen);
	tpm_buf_append(buf, passphrase, passphraselen);
}
static inline void tpm_buf_append_hmac_session_opt(struct tpm_chip *chip,
						   struct tpm_buf *buf,
						   u8 attributes,
						   u8 *passphrase,
						   int passphraselen)
{
	int offset = buf->handles * 4 + TPM_HEADER_SIZE;
	struct tpm_header *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);
}
static inline void tpm_buf_fill_hmac_session(struct tpm_chip *chip,
					     struct tpm_buf *buf)
{