brintos

brintos / linux-shallow public Read only

0
0
Text · 14.6 KiB · 60354cd Raw
648 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2016 Intel Corporation4 *5 * Authors:6 * Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>7 *8 * Maintained by: <tpmdd-devel@lists.sourceforge.net>9 *10 * This file contains TPM2 protocol implementations of the commands11 * used by the kernel internally.12 */13 14#include <linux/gfp.h>15#include <linux/unaligned.h>16#include "tpm.h"17 18enum tpm2_handle_types {19	TPM2_HT_HMAC_SESSION	= 0x02000000,20	TPM2_HT_POLICY_SESSION	= 0x03000000,21	TPM2_HT_TRANSIENT	= 0x80000000,22};23 24struct tpm2_context {25	__be64 sequence;26	__be32 saved_handle;27	__be32 hierarchy;28	__be16 blob_size;29} __packed;30 31static void tpm2_flush_sessions(struct tpm_chip *chip, struct tpm_space *space)32{33	int i;34 35	for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++) {36		if (space->session_tbl[i])37			tpm2_flush_context(chip, space->session_tbl[i]);38	}39}40 41int tpm2_init_space(struct tpm_space *space, unsigned int buf_size)42{43	space->context_buf = kzalloc(buf_size, GFP_KERNEL);44	if (!space->context_buf)45		return -ENOMEM;46 47	space->session_buf = kzalloc(buf_size, GFP_KERNEL);48	if (space->session_buf == NULL) {49		kfree(space->context_buf);50		/* Prevent caller getting a dangling pointer. */51		space->context_buf = NULL;52		return -ENOMEM;53	}54 55	space->buf_size = buf_size;56	return 0;57}58 59void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space)60{61 62	if (tpm_try_get_ops(chip) == 0) {63		tpm2_flush_sessions(chip, space);64		tpm_put_ops(chip);65	}66 67	kfree(space->context_buf);68	kfree(space->session_buf);69}70 71int tpm2_load_context(struct tpm_chip *chip, u8 *buf,72		      unsigned int *offset, u32 *handle)73{74	struct tpm_buf tbuf;75	struct tpm2_context *ctx;76	unsigned int body_size;77	int rc;78 79	rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_LOAD);80	if (rc)81		return rc;82 83	ctx = (struct tpm2_context *)&buf[*offset];84	body_size = sizeof(*ctx) + be16_to_cpu(ctx->blob_size);85	tpm_buf_append(&tbuf, &buf[*offset], body_size);86 87	rc = tpm_transmit_cmd(chip, &tbuf, 4, NULL);88	if (rc < 0) {89		dev_warn(&chip->dev, "%s: failed with a system error %d\n",90			 __func__, rc);91		tpm_buf_destroy(&tbuf);92		return -EFAULT;93	} else if (tpm2_rc_value(rc) == TPM2_RC_HANDLE ||94		   rc == TPM2_RC_REFERENCE_H0) {95		/*96		 * TPM_RC_HANDLE means that the session context can't97		 * be loaded because of an internal counter mismatch98		 * that makes the TPM think there might have been a99		 * replay.  This might happen if the context was saved100		 * and loaded outside the space.101		 *102		 * TPM_RC_REFERENCE_H0 means the session has been103		 * flushed outside the space104		 */105		*handle = 0;106		tpm_buf_destroy(&tbuf);107		return -ENOENT;108	} else if (tpm2_rc_value(rc) == TPM2_RC_INTEGRITY) {109		tpm_buf_destroy(&tbuf);110		return -EINVAL;111	} else if (rc > 0) {112		dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",113			 __func__, rc);114		tpm_buf_destroy(&tbuf);115		return -EFAULT;116	}117 118	*handle = be32_to_cpup((__be32 *)&tbuf.data[TPM_HEADER_SIZE]);119	*offset += body_size;120 121	tpm_buf_destroy(&tbuf);122	return 0;123}124 125int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf,126		      unsigned int buf_size, unsigned int *offset)127{128	struct tpm_buf tbuf;129	unsigned int body_size;130	int rc;131 132	rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_SAVE);133	if (rc)134		return rc;135 136	tpm_buf_append_u32(&tbuf, handle);137 138	rc = tpm_transmit_cmd(chip, &tbuf, 0, NULL);139	if (rc < 0) {140		dev_warn(&chip->dev, "%s: failed with a system error %d\n",141			 __func__, rc);142		tpm_buf_destroy(&tbuf);143		return -EFAULT;144	} else if (tpm2_rc_value(rc) == TPM2_RC_REFERENCE_H0) {145		tpm_buf_destroy(&tbuf);146		return -ENOENT;147	} else if (rc) {148		dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",149			 __func__, rc);150		tpm_buf_destroy(&tbuf);151		return -EFAULT;152	}153 154	body_size = tpm_buf_length(&tbuf) - TPM_HEADER_SIZE;155	if ((*offset + body_size) > buf_size) {156		dev_warn(&chip->dev, "%s: out of backing storage\n", __func__);157		tpm_buf_destroy(&tbuf);158		return -ENOMEM;159	}160 161	memcpy(&buf[*offset], &tbuf.data[TPM_HEADER_SIZE], body_size);162	*offset += body_size;163	tpm_buf_destroy(&tbuf);164	return 0;165}166 167void tpm2_flush_space(struct tpm_chip *chip)168{169	struct tpm_space *space = &chip->work_space;170	int i;171 172	if (!space)173		return;174 175	for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++)176		if (space->context_tbl[i] && ~space->context_tbl[i])177			tpm2_flush_context(chip, space->context_tbl[i]);178 179	tpm2_flush_sessions(chip, space);180}181 182static int tpm2_load_space(struct tpm_chip *chip)183{184	struct tpm_space *space = &chip->work_space;185	unsigned int offset;186	int i;187	int rc;188 189	for (i = 0, offset = 0; i < ARRAY_SIZE(space->context_tbl); i++) {190		if (!space->context_tbl[i])191			continue;192 193		/* sanity check, should never happen */194		if (~space->context_tbl[i]) {195			dev_err(&chip->dev, "context table is inconsistent");196			return -EFAULT;197		}198 199		rc = tpm2_load_context(chip, space->context_buf, &offset,200				       &space->context_tbl[i]);201		if (rc)202			return rc;203	}204 205	for (i = 0, offset = 0; i < ARRAY_SIZE(space->session_tbl); i++) {206		u32 handle;207 208		if (!space->session_tbl[i])209			continue;210 211		rc = tpm2_load_context(chip, space->session_buf,212				       &offset, &handle);213		if (rc == -ENOENT) {214			/* load failed, just forget session */215			space->session_tbl[i] = 0;216		} else if (rc) {217			tpm2_flush_space(chip);218			return rc;219		}220		if (handle != space->session_tbl[i]) {221			dev_warn(&chip->dev, "session restored to wrong handle\n");222			tpm2_flush_space(chip);223			return -EFAULT;224		}225	}226 227	return 0;228}229 230static bool tpm2_map_to_phandle(struct tpm_space *space, void *handle)231{232	u32 vhandle = be32_to_cpup((__be32 *)handle);233	u32 phandle;234	int i;235 236	i = 0xFFFFFF - (vhandle & 0xFFFFFF);237	if (i >= ARRAY_SIZE(space->context_tbl) || !space->context_tbl[i])238		return false;239 240	phandle = space->context_tbl[i];241	*((__be32 *)handle) = cpu_to_be32(phandle);242	return true;243}244 245static int tpm2_map_command(struct tpm_chip *chip, u32 cc, u8 *cmd)246{247	struct tpm_space *space = &chip->work_space;248	unsigned int nr_handles;249	u32 attrs;250	__be32 *handle;251	int i;252 253	i = tpm2_find_cc(chip, cc);254	if (i < 0)255		return -EINVAL;256 257	attrs = chip->cc_attrs_tbl[i];258	nr_handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);259 260	handle = (__be32 *)&cmd[TPM_HEADER_SIZE];261	for (i = 0; i < nr_handles; i++, handle++) {262		if ((be32_to_cpu(*handle) & 0xFF000000) == TPM2_HT_TRANSIENT) {263			if (!tpm2_map_to_phandle(space, handle))264				return -EINVAL;265		}266	}267 268	return 0;269}270 271static int tpm_find_and_validate_cc(struct tpm_chip *chip,272				    struct tpm_space *space,273				    const void *cmd, size_t len)274{275	const struct tpm_header *header = (const void *)cmd;276	int i;277	u32 cc;278	u32 attrs;279	unsigned int nr_handles;280 281	if (len < TPM_HEADER_SIZE || !chip->nr_commands)282		return -EINVAL;283 284	cc = be32_to_cpu(header->ordinal);285 286	i = tpm2_find_cc(chip, cc);287	if (i < 0) {288		dev_dbg(&chip->dev, "0x%04X is an invalid command\n",289			cc);290		return -EOPNOTSUPP;291	}292 293	attrs = chip->cc_attrs_tbl[i];294	nr_handles =295		4 * ((attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0));296	if (len < TPM_HEADER_SIZE + 4 * nr_handles)297		goto err_len;298 299	return cc;300err_len:301	dev_dbg(&chip->dev, "%s: insufficient command length %zu", __func__,302		len);303	return -EINVAL;304}305 306int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u8 *cmd,307		       size_t cmdsiz)308{309	int rc;310	int cc;311 312	if (!space)313		return 0;314 315	cc = tpm_find_and_validate_cc(chip, space, cmd, cmdsiz);316	if (cc < 0)317		return cc;318 319	memcpy(&chip->work_space.context_tbl, &space->context_tbl,320	       sizeof(space->context_tbl));321	memcpy(&chip->work_space.session_tbl, &space->session_tbl,322	       sizeof(space->session_tbl));323	memcpy(chip->work_space.context_buf, space->context_buf,324	       space->buf_size);325	memcpy(chip->work_space.session_buf, space->session_buf,326	       space->buf_size);327 328	rc = tpm2_load_space(chip);329	if (rc) {330		tpm2_flush_space(chip);331		return rc;332	}333 334	rc = tpm2_map_command(chip, cc, cmd);335	if (rc) {336		tpm2_flush_space(chip);337		return rc;338	}339 340	chip->last_cc = cc;341	return 0;342}343 344static bool tpm2_add_session(struct tpm_chip *chip, u32 handle)345{346	struct tpm_space *space = &chip->work_space;347	int i;348 349	for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++)350		if (space->session_tbl[i] == 0)351			break;352 353	if (i == ARRAY_SIZE(space->session_tbl))354		return false;355 356	space->session_tbl[i] = handle;357	return true;358}359 360static u32 tpm2_map_to_vhandle(struct tpm_space *space, u32 phandle, bool alloc)361{362	int i;363 364	for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++) {365		if (alloc) {366			if (!space->context_tbl[i]) {367				space->context_tbl[i] = phandle;368				break;369			}370		} else if (space->context_tbl[i] == phandle)371			break;372	}373 374	if (i == ARRAY_SIZE(space->context_tbl))375		return 0;376 377	return TPM2_HT_TRANSIENT | (0xFFFFFF - i);378}379 380static int tpm2_map_response_header(struct tpm_chip *chip, u32 cc, u8 *rsp,381				    size_t len)382{383	struct tpm_space *space = &chip->work_space;384	struct tpm_header *header = (struct tpm_header *)rsp;385	u32 phandle;386	u32 phandle_type;387	u32 vhandle;388	u32 attrs;389	int i;390 391	if (be32_to_cpu(header->return_code) != TPM2_RC_SUCCESS)392		return 0;393 394	i = tpm2_find_cc(chip, cc);395	/* sanity check, should never happen */396	if (i < 0)397		return -EFAULT;398 399	attrs = chip->cc_attrs_tbl[i];400	if (!((attrs >> TPM2_CC_ATTR_RHANDLE) & 1))401		return 0;402 403	phandle = be32_to_cpup((__be32 *)&rsp[TPM_HEADER_SIZE]);404	phandle_type = phandle & 0xFF000000;405 406	switch (phandle_type) {407	case TPM2_HT_TRANSIENT:408		vhandle = tpm2_map_to_vhandle(space, phandle, true);409		if (!vhandle)410			goto out_no_slots;411 412		*(__be32 *)&rsp[TPM_HEADER_SIZE] = cpu_to_be32(vhandle);413		break;414	case TPM2_HT_HMAC_SESSION:415	case TPM2_HT_POLICY_SESSION:416		if (!tpm2_add_session(chip, phandle))417			goto out_no_slots;418		break;419	default:420		dev_err(&chip->dev, "%s: unknown handle 0x%08X\n",421			__func__, phandle);422		break;423	}424 425	return 0;426out_no_slots:427	tpm2_flush_context(chip, phandle);428	dev_warn(&chip->dev, "%s: out of slots for 0x%08X\n", __func__,429		 phandle);430	return -ENOMEM;431}432 433struct tpm2_cap_handles {434	u8 more_data;435	__be32 capability;436	__be32 count;437	__be32 handles[];438} __packed;439 440static int tpm2_map_response_body(struct tpm_chip *chip, u32 cc, u8 *rsp,441				  size_t len)442{443	struct tpm_space *space = &chip->work_space;444	struct tpm_header *header = (struct tpm_header *)rsp;445	struct tpm2_cap_handles *data;446	u32 phandle;447	u32 phandle_type;448	u32 vhandle;449	int i;450	int j;451 452	if (cc != TPM2_CC_GET_CAPABILITY ||453	    be32_to_cpu(header->return_code) != TPM2_RC_SUCCESS) {454		return 0;455	}456 457	if (len < TPM_HEADER_SIZE + 9)458		return -EFAULT;459 460	data = (void *)&rsp[TPM_HEADER_SIZE];461	if (be32_to_cpu(data->capability) != TPM2_CAP_HANDLES)462		return 0;463 464	if (be32_to_cpu(data->count) > (UINT_MAX - TPM_HEADER_SIZE - 9) / 4)465		return -EFAULT;466 467	if (len != TPM_HEADER_SIZE + 9 + 4 * be32_to_cpu(data->count))468		return -EFAULT;469 470	for (i = 0, j = 0; i < be32_to_cpu(data->count); i++) {471		phandle = be32_to_cpup((__be32 *)&data->handles[i]);472		phandle_type = phandle & 0xFF000000;473 474		switch (phandle_type) {475		case TPM2_HT_TRANSIENT:476			vhandle = tpm2_map_to_vhandle(space, phandle, false);477			if (!vhandle)478				break;479 480			data->handles[j] = cpu_to_be32(vhandle);481			j++;482			break;483 484		default:485			data->handles[j] = cpu_to_be32(phandle);486			j++;487			break;488		}489 490	}491 492	header->length = cpu_to_be32(TPM_HEADER_SIZE + 9 + 4 * j);493	data->count = cpu_to_be32(j);494	return 0;495}496 497static int tpm2_save_space(struct tpm_chip *chip)498{499	struct tpm_space *space = &chip->work_space;500	unsigned int offset;501	int i;502	int rc;503 504	for (i = 0, offset = 0; i < ARRAY_SIZE(space->context_tbl); i++) {505		if (!(space->context_tbl[i] && ~space->context_tbl[i]))506			continue;507 508		rc = tpm2_save_context(chip, space->context_tbl[i],509				       space->context_buf, space->buf_size,510				       &offset);511		if (rc == -ENOENT) {512			space->context_tbl[i] = 0;513			continue;514		} else if (rc)515			return rc;516 517		tpm2_flush_context(chip, space->context_tbl[i]);518		space->context_tbl[i] = ~0;519	}520 521	for (i = 0, offset = 0; i < ARRAY_SIZE(space->session_tbl); i++) {522		if (!space->session_tbl[i])523			continue;524 525		rc = tpm2_save_context(chip, space->session_tbl[i],526				       space->session_buf, space->buf_size,527				       &offset);528		if (rc == -ENOENT) {529			/* handle error saving session, just forget it */530			space->session_tbl[i] = 0;531		} else if (rc < 0) {532			tpm2_flush_space(chip);533			return rc;534		}535	}536 537	return 0;538}539 540int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space,541		      void *buf, size_t *bufsiz)542{543	struct tpm_header *header = buf;544	int rc;545 546	if (!space)547		return 0;548 549	rc = tpm2_map_response_header(chip, chip->last_cc, buf, *bufsiz);550	if (rc) {551		tpm2_flush_space(chip);552		goto out;553	}554 555	rc = tpm2_map_response_body(chip, chip->last_cc, buf, *bufsiz);556	if (rc) {557		tpm2_flush_space(chip);558		goto out;559	}560 561	rc = tpm2_save_space(chip);562	if (rc) {563		tpm2_flush_space(chip);564		goto out;565	}566 567	*bufsiz = be32_to_cpu(header->length);568 569	memcpy(&space->context_tbl, &chip->work_space.context_tbl,570	       sizeof(space->context_tbl));571	memcpy(&space->session_tbl, &chip->work_space.session_tbl,572	       sizeof(space->session_tbl));573	memcpy(space->context_buf, chip->work_space.context_buf,574	       space->buf_size);575	memcpy(space->session_buf, chip->work_space.session_buf,576	       space->buf_size);577 578	return 0;579out:580	dev_err(&chip->dev, "%s: error %d\n", __func__, rc);581	return rc;582}583 584/*585 * Put the reference to the main device.586 */587static void tpm_devs_release(struct device *dev)588{589	struct tpm_chip *chip = container_of(dev, struct tpm_chip, devs);590 591	/* release the master device reference */592	put_device(&chip->dev);593}594 595/*596 * Remove the device file for exposed TPM spaces and release the device597 * reference. This may also release the reference to the master device.598 */599void tpm_devs_remove(struct tpm_chip *chip)600{601	cdev_device_del(&chip->cdevs, &chip->devs);602	put_device(&chip->devs);603}604 605/*606 * Add a device file to expose TPM spaces. Also take a reference to the607 * main device.608 */609int tpm_devs_add(struct tpm_chip *chip)610{611	int rc;612 613	device_initialize(&chip->devs);614	chip->devs.parent = chip->dev.parent;615	chip->devs.class = &tpmrm_class;616 617	/*618	 * Get extra reference on main device to hold on behalf of devs.619	 * This holds the chip structure while cdevs is in use. The620	 * corresponding put is in the tpm_devs_release.621	 */622	get_device(&chip->dev);623	chip->devs.release = tpm_devs_release;624	chip->devs.devt = MKDEV(MAJOR(tpm_devt), chip->dev_num + TPM_NUM_DEVICES);625	cdev_init(&chip->cdevs, &tpmrm_fops);626	chip->cdevs.owner = THIS_MODULE;627 628	rc = dev_set_name(&chip->devs, "tpmrm%d", chip->dev_num);629	if (rc)630		goto err_put_devs;631 632	rc = cdev_device_add(&chip->cdevs, &chip->devs);633	if (rc) {634		dev_err(&chip->devs,635			"unable to cdev_device_add() %s, major %d, minor %d, err=%d\n",636			dev_name(&chip->devs), MAJOR(chip->devs.devt),637			MINOR(chip->devs.devt), rc);638		goto err_put_devs;639	}640 641	return 0;642 643err_put_devs:644	put_device(&chip->devs);645 646	return rc;647}648