xref: /openbmc/linux/drivers/char/tpm/tpm2-space.c (revision e6e8c82b)
1 /*
2  * Copyright (C) 2016 Intel Corporation
3  *
4  * Authors:
5  * Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
6  *
7  * Maintained by: <tpmdd-devel@lists.sourceforge.net>
8  *
9  * This file contains TPM2 protocol implementations of the commands
10  * used by the kernel internally.
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; version 2
15  * of the License.
16  */
17 
18 #include <linux/gfp.h>
19 #include <asm/unaligned.h>
20 #include "tpm.h"
21 
22 enum tpm2_handle_types {
23 	TPM2_HT_HMAC_SESSION	= 0x02000000,
24 	TPM2_HT_POLICY_SESSION	= 0x03000000,
25 	TPM2_HT_TRANSIENT	= 0x80000000,
26 };
27 
28 struct tpm2_context {
29 	__be64 sequence;
30 	__be32 saved_handle;
31 	__be32 hierarchy;
32 	__be16 blob_size;
33 } __packed;
34 
35 static void tpm2_flush_sessions(struct tpm_chip *chip, struct tpm_space *space)
36 {
37 	int i;
38 
39 	for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++) {
40 		if (space->session_tbl[i])
41 			tpm2_flush_context_cmd(chip, space->session_tbl[i],
42 					       TPM_TRANSMIT_NESTED);
43 	}
44 }
45 
46 int tpm2_init_space(struct tpm_space *space)
47 {
48 	space->context_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
49 	if (!space->context_buf)
50 		return -ENOMEM;
51 
52 	space->session_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
53 	if (space->session_buf == NULL) {
54 		kfree(space->context_buf);
55 		return -ENOMEM;
56 	}
57 
58 	return 0;
59 }
60 
61 void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space)
62 {
63 	mutex_lock(&chip->tpm_mutex);
64 	tpm2_flush_sessions(chip, space);
65 	mutex_unlock(&chip->tpm_mutex);
66 	kfree(space->context_buf);
67 	kfree(space->session_buf);
68 }
69 
70 static int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
71 			     unsigned int *offset, u32 *handle)
72 {
73 	struct tpm_buf tbuf;
74 	struct tpm2_context *ctx;
75 	unsigned int body_size;
76 	int rc;
77 
78 	rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_LOAD);
79 	if (rc)
80 		return rc;
81 
82 	ctx = (struct tpm2_context *)&buf[*offset];
83 	body_size = sizeof(*ctx) + be16_to_cpu(ctx->blob_size);
84 	tpm_buf_append(&tbuf, &buf[*offset], body_size);
85 
86 	rc = tpm_transmit_cmd(chip, NULL, tbuf.data, PAGE_SIZE, 4,
87 			      TPM_TRANSMIT_NESTED, 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't
97 		 * be loaded because of an internal counter mismatch
98 		 * that makes the TPM think there might have been a
99 		 * replay.  This might happen if the context was saved
100 		 * and loaded outside the space.
101 		 *
102 		 * TPM_RC_REFERENCE_H0 means the session has been
103 		 * flushed outside the space
104 		 */
105 		*handle = 0;
106 		tpm_buf_destroy(&tbuf);
107 		return -ENOENT;
108 	} else if (rc > 0) {
109 		dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
110 			 __func__, rc);
111 		tpm_buf_destroy(&tbuf);
112 		return -EFAULT;
113 	}
114 
115 	*handle = be32_to_cpup((__be32 *)&tbuf.data[TPM_HEADER_SIZE]);
116 	*offset += body_size;
117 
118 	tpm_buf_destroy(&tbuf);
119 	return 0;
120 }
121 
122 static int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf,
123 			     unsigned int buf_size, unsigned int *offset)
124 {
125 	struct tpm_buf tbuf;
126 	unsigned int body_size;
127 	int rc;
128 
129 	rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_SAVE);
130 	if (rc)
131 		return rc;
132 
133 	tpm_buf_append_u32(&tbuf, handle);
134 
135 	rc = tpm_transmit_cmd(chip, NULL, tbuf.data, PAGE_SIZE, 0,
136 			      TPM_TRANSMIT_NESTED, NULL);
137 	if (rc < 0) {
138 		dev_warn(&chip->dev, "%s: failed with a system error %d\n",
139 			 __func__, rc);
140 		tpm_buf_destroy(&tbuf);
141 		return -EFAULT;
142 	} else if (tpm2_rc_value(rc) == TPM2_RC_REFERENCE_H0) {
143 		tpm_buf_destroy(&tbuf);
144 		return -ENOENT;
145 	} else if (rc) {
146 		dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
147 			 __func__, rc);
148 		tpm_buf_destroy(&tbuf);
149 		return -EFAULT;
150 	}
151 
152 	body_size = tpm_buf_length(&tbuf) - TPM_HEADER_SIZE;
153 	if ((*offset + body_size) > buf_size) {
154 		dev_warn(&chip->dev, "%s: out of backing storage\n", __func__);
155 		tpm_buf_destroy(&tbuf);
156 		return -ENOMEM;
157 	}
158 
159 	memcpy(&buf[*offset], &tbuf.data[TPM_HEADER_SIZE], body_size);
160 	*offset += body_size;
161 	tpm_buf_destroy(&tbuf);
162 	return 0;
163 }
164 
165 static void tpm2_flush_space(struct tpm_chip *chip)
166 {
167 	struct tpm_space *space = &chip->work_space;
168 	int i;
169 
170 	for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++)
171 		if (space->context_tbl[i] && ~space->context_tbl[i])
172 			tpm2_flush_context_cmd(chip, space->context_tbl[i],
173 					       TPM_TRANSMIT_NESTED);
174 
175 	tpm2_flush_sessions(chip, space);
176 }
177 
178 static int tpm2_load_space(struct tpm_chip *chip)
179 {
180 	struct tpm_space *space = &chip->work_space;
181 	unsigned int offset;
182 	int i;
183 	int rc;
184 
185 	for (i = 0, offset = 0; i < ARRAY_SIZE(space->context_tbl); i++) {
186 		if (!space->context_tbl[i])
187 			continue;
188 
189 		/* sanity check, should never happen */
190 		if (~space->context_tbl[i]) {
191 			dev_err(&chip->dev, "context table is inconsistent");
192 			return -EFAULT;
193 		}
194 
195 		rc = tpm2_load_context(chip, space->context_buf, &offset,
196 				       &space->context_tbl[i]);
197 		if (rc)
198 			return rc;
199 	}
200 
201 	for (i = 0, offset = 0; i < ARRAY_SIZE(space->session_tbl); i++) {
202 		u32 handle;
203 
204 		if (!space->session_tbl[i])
205 			continue;
206 
207 		rc = tpm2_load_context(chip, space->session_buf,
208 				       &offset, &handle);
209 		if (rc == -ENOENT) {
210 			/* load failed, just forget session */
211 			space->session_tbl[i] = 0;
212 		} else if (rc) {
213 			tpm2_flush_space(chip);
214 			return rc;
215 		}
216 		if (handle != space->session_tbl[i]) {
217 			dev_warn(&chip->dev, "session restored to wrong handle\n");
218 			tpm2_flush_space(chip);
219 			return -EFAULT;
220 		}
221 	}
222 
223 	return 0;
224 }
225 
226 static bool tpm2_map_to_phandle(struct tpm_space *space, void *handle)
227 {
228 	u32 vhandle = be32_to_cpup((__be32 *)handle);
229 	u32 phandle;
230 	int i;
231 
232 	i = 0xFFFFFF - (vhandle & 0xFFFFFF);
233 	if (i >= ARRAY_SIZE(space->context_tbl) || !space->context_tbl[i])
234 		return false;
235 
236 	phandle = space->context_tbl[i];
237 	*((__be32 *)handle) = cpu_to_be32(phandle);
238 	return true;
239 }
240 
241 static int tpm2_map_command(struct tpm_chip *chip, u32 cc, u8 *cmd)
242 {
243 	struct tpm_space *space = &chip->work_space;
244 	unsigned int nr_handles;
245 	u32 attrs;
246 	__be32 *handle;
247 	int i;
248 
249 	i = tpm2_find_cc(chip, cc);
250 	if (i < 0)
251 		return -EINVAL;
252 
253 	attrs = chip->cc_attrs_tbl[i];
254 	nr_handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);
255 
256 	handle = (__be32 *)&cmd[TPM_HEADER_SIZE];
257 	for (i = 0; i < nr_handles; i++, handle++) {
258 		if ((be32_to_cpu(*handle) & 0xFF000000) == TPM2_HT_TRANSIENT) {
259 			if (!tpm2_map_to_phandle(space, handle))
260 				return -EINVAL;
261 		}
262 	}
263 
264 	return 0;
265 }
266 
267 int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u32 cc,
268 		       u8 *cmd)
269 {
270 	int rc;
271 
272 	if (!space)
273 		return 0;
274 
275 	memcpy(&chip->work_space.context_tbl, &space->context_tbl,
276 	       sizeof(space->context_tbl));
277 	memcpy(&chip->work_space.session_tbl, &space->session_tbl,
278 	       sizeof(space->session_tbl));
279 	memcpy(chip->work_space.context_buf, space->context_buf, PAGE_SIZE);
280 	memcpy(chip->work_space.session_buf, space->session_buf, PAGE_SIZE);
281 
282 	rc = tpm2_load_space(chip);
283 	if (rc) {
284 		tpm2_flush_space(chip);
285 		return rc;
286 	}
287 
288 	rc = tpm2_map_command(chip, cc, cmd);
289 	if (rc) {
290 		tpm2_flush_space(chip);
291 		return rc;
292 	}
293 
294 	return 0;
295 }
296 
297 static bool tpm2_add_session(struct tpm_chip *chip, u32 handle)
298 {
299 	struct tpm_space *space = &chip->work_space;
300 	int i;
301 
302 	for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++)
303 		if (space->session_tbl[i] == 0)
304 			break;
305 
306 	if (i == ARRAY_SIZE(space->session_tbl))
307 		return false;
308 
309 	space->session_tbl[i] = handle;
310 	return true;
311 }
312 
313 static u32 tpm2_map_to_vhandle(struct tpm_space *space, u32 phandle, bool alloc)
314 {
315 	int i;
316 
317 	for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++) {
318 		if (alloc) {
319 			if (!space->context_tbl[i]) {
320 				space->context_tbl[i] = phandle;
321 				break;
322 			}
323 		} else if (space->context_tbl[i] == phandle)
324 			break;
325 	}
326 
327 	if (i == ARRAY_SIZE(space->context_tbl))
328 		return 0;
329 
330 	return TPM2_HT_TRANSIENT | (0xFFFFFF - i);
331 }
332 
333 static int tpm2_map_response_header(struct tpm_chip *chip, u32 cc, u8 *rsp,
334 				    size_t len)
335 {
336 	struct tpm_space *space = &chip->work_space;
337 	struct tpm_output_header *header = (void *)rsp;
338 	u32 phandle;
339 	u32 phandle_type;
340 	u32 vhandle;
341 	u32 attrs;
342 	int i;
343 
344 	if (be32_to_cpu(header->return_code) != TPM2_RC_SUCCESS)
345 		return 0;
346 
347 	i = tpm2_find_cc(chip, cc);
348 	/* sanity check, should never happen */
349 	if (i < 0)
350 		return -EFAULT;
351 
352 	attrs = chip->cc_attrs_tbl[i];
353 	if (!((attrs >> TPM2_CC_ATTR_RHANDLE) & 1))
354 		return 0;
355 
356 	phandle = be32_to_cpup((__be32 *)&rsp[TPM_HEADER_SIZE]);
357 	phandle_type = phandle & 0xFF000000;
358 
359 	switch (phandle_type) {
360 	case TPM2_HT_TRANSIENT:
361 		vhandle = tpm2_map_to_vhandle(space, phandle, true);
362 		if (!vhandle)
363 			goto out_no_slots;
364 
365 		*(__be32 *)&rsp[TPM_HEADER_SIZE] = cpu_to_be32(vhandle);
366 		break;
367 	case TPM2_HT_HMAC_SESSION:
368 	case TPM2_HT_POLICY_SESSION:
369 		if (!tpm2_add_session(chip, phandle))
370 			goto out_no_slots;
371 		break;
372 	default:
373 		dev_err(&chip->dev, "%s: unknown handle 0x%08X\n",
374 			__func__, phandle);
375 		break;
376 	}
377 
378 	return 0;
379 out_no_slots:
380 	tpm2_flush_context_cmd(chip, phandle, TPM_TRANSMIT_NESTED);
381 	dev_warn(&chip->dev, "%s: out of slots for 0x%08X\n", __func__,
382 		 phandle);
383 	return -ENOMEM;
384 }
385 
386 struct tpm2_cap_handles {
387 	u8 more_data;
388 	__be32 capability;
389 	__be32 count;
390 	__be32 handles[];
391 } __packed;
392 
393 static int tpm2_map_response_body(struct tpm_chip *chip, u32 cc, u8 *rsp,
394 				  size_t len)
395 {
396 	struct tpm_space *space = &chip->work_space;
397 	struct tpm_output_header *header = (void *)rsp;
398 	struct tpm2_cap_handles *data;
399 	u32 phandle;
400 	u32 phandle_type;
401 	u32 vhandle;
402 	int i;
403 	int j;
404 
405 	if (cc != TPM2_CC_GET_CAPABILITY ||
406 	    be32_to_cpu(header->return_code) != TPM2_RC_SUCCESS) {
407 		return 0;
408 	}
409 
410 	if (len < TPM_HEADER_SIZE + 9)
411 		return -EFAULT;
412 
413 	data = (void *)&rsp[TPM_HEADER_SIZE];
414 	if (be32_to_cpu(data->capability) != TPM2_CAP_HANDLES)
415 		return 0;
416 
417 	if (len != TPM_HEADER_SIZE + 9 + 4 * be32_to_cpu(data->count))
418 		return -EFAULT;
419 
420 	for (i = 0, j = 0; i < be32_to_cpu(data->count); i++) {
421 		phandle = be32_to_cpup((__be32 *)&data->handles[i]);
422 		phandle_type = phandle & 0xFF000000;
423 
424 		switch (phandle_type) {
425 		case TPM2_HT_TRANSIENT:
426 			vhandle = tpm2_map_to_vhandle(space, phandle, false);
427 			if (!vhandle)
428 				break;
429 
430 			data->handles[j] = cpu_to_be32(vhandle);
431 			j++;
432 			break;
433 
434 		default:
435 			data->handles[j] = cpu_to_be32(phandle);
436 			j++;
437 			break;
438 		}
439 
440 	}
441 
442 	header->length = cpu_to_be32(TPM_HEADER_SIZE + 9 + 4 * j);
443 	data->count = cpu_to_be32(j);
444 	return 0;
445 }
446 
447 static int tpm2_save_space(struct tpm_chip *chip)
448 {
449 	struct tpm_space *space = &chip->work_space;
450 	unsigned int offset;
451 	int i;
452 	int rc;
453 
454 	for (i = 0, offset = 0; i < ARRAY_SIZE(space->context_tbl); i++) {
455 		if (!(space->context_tbl[i] && ~space->context_tbl[i]))
456 			continue;
457 
458 		rc = tpm2_save_context(chip, space->context_tbl[i],
459 				       space->context_buf, PAGE_SIZE,
460 				       &offset);
461 		if (rc == -ENOENT) {
462 			space->context_tbl[i] = 0;
463 			continue;
464 		} else if (rc)
465 			return rc;
466 
467 		tpm2_flush_context_cmd(chip, space->context_tbl[i],
468 				       TPM_TRANSMIT_NESTED);
469 		space->context_tbl[i] = ~0;
470 	}
471 
472 	for (i = 0, offset = 0; i < ARRAY_SIZE(space->session_tbl); i++) {
473 		if (!space->session_tbl[i])
474 			continue;
475 
476 		rc = tpm2_save_context(chip, space->session_tbl[i],
477 				       space->session_buf, PAGE_SIZE,
478 				       &offset);
479 
480 		if (rc == -ENOENT) {
481 			/* handle error saving session, just forget it */
482 			space->session_tbl[i] = 0;
483 		} else if (rc < 0) {
484 			tpm2_flush_space(chip);
485 			return rc;
486 		}
487 	}
488 
489 	return 0;
490 }
491 
492 int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space,
493 		      u32 cc, u8 *buf, size_t *bufsiz)
494 {
495 	struct tpm_output_header *header = (void *)buf;
496 	int rc;
497 
498 	if (!space)
499 		return 0;
500 
501 	rc = tpm2_map_response_header(chip, cc, buf, *bufsiz);
502 	if (rc) {
503 		tpm2_flush_space(chip);
504 		return rc;
505 	}
506 
507 	rc = tpm2_map_response_body(chip, cc, buf, *bufsiz);
508 	if (rc) {
509 		tpm2_flush_space(chip);
510 		return rc;
511 	}
512 
513 	rc = tpm2_save_space(chip);
514 	if (rc) {
515 		tpm2_flush_space(chip);
516 		return rc;
517 	}
518 
519 	*bufsiz = be32_to_cpu(header->length);
520 
521 	memcpy(&space->context_tbl, &chip->work_space.context_tbl,
522 	       sizeof(space->context_tbl));
523 	memcpy(&space->session_tbl, &chip->work_space.session_tbl,
524 	       sizeof(space->session_tbl));
525 	memcpy(space->context_buf, chip->work_space.context_buf, PAGE_SIZE);
526 	memcpy(space->session_buf, chip->work_space.session_buf, PAGE_SIZE);
527 
528 	return 0;
529 }
530