aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Bottomley <James.Bottomley@HansenPartnership.com>2017-01-31 18:47:31 -0500
committerJarkko Sakkinen <jarkko.sakkinen@linux.intel.com>2017-04-03 15:46:02 -0400
commit4d57856a21ed2abe33412e0526cc84bdcf67ea08 (patch)
tree69d2ef5b2dfab9ea14b68f7efdb8e90503cfba57
parentfdc915f7f71939ad5a3dda3389b8d2d7a7c5ee66 (diff)
tpm2: add session handle context saving and restoring to the space code
Sessions are different from transient objects in that their handles may not be virtualized (because they're used for some hmac calculations). Additionally when a session is context saved, a vestigial memory remains in the TPM and if it is also flushed, that will be lost and the session context will refuse to load next time, so the code is updated to flush only transient objects after a context save. Add a separate array (chip->session_tbl) to save and restore sessions by handle. Use the failure of a context save or load to signal that the session has been flushed from the TPM and we can remove its memory from chip->session_tbl. Sessions are also isolated during each instance of a tpm space. This means that spaces shouldn't be able to see each other's sessions and is enforced by ensuring that a space user may only refer to sessions handles that are present in their own chip->session_tbl. Finally when a space is closed, all the sessions belonging to it should be flushed so the handles may be re-used by other spaces. Note that if we get a session save or load error, all sessions are effectively flushed. Even though we restore the session buffer, all the old sessions will refuse to load after the flush and they'll be purged from our session memory. This means that while transient context handling is still soft in the face of errors, session handling is hard (any failure of the model means all sessions are lost). Fixes-from: Colin Ian King <colin.king@canonical.com> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com> Tested-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com> Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com> Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
-rw-r--r--drivers/char/tpm/tpm-chip.c8
-rw-r--r--drivers/char/tpm/tpm.h4
-rw-r--r--drivers/char/tpm/tpm2-space.c115
-rw-r--r--drivers/char/tpm/tpmrm-dev.c2
4 files changed, 116 insertions, 13 deletions
diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
index 187ec04ce9c3..aade6995f310 100644
--- a/drivers/char/tpm/tpm-chip.c
+++ b/drivers/char/tpm/tpm-chip.c
@@ -130,6 +130,7 @@ static void tpm_dev_release(struct device *dev)
130 130
131 kfree(chip->log.bios_event_log); 131 kfree(chip->log.bios_event_log);
132 kfree(chip->work_space.context_buf); 132 kfree(chip->work_space.context_buf);
133 kfree(chip->work_space.session_buf);
133 kfree(chip); 134 kfree(chip);
134} 135}
135 136
@@ -224,6 +225,11 @@ struct tpm_chip *tpm_chip_alloc(struct device *pdev,
224 rc = -ENOMEM; 225 rc = -ENOMEM;
225 goto out; 226 goto out;
226 } 227 }
228 chip->work_space.session_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
229 if (!chip->work_space.session_buf) {
230 rc = -ENOMEM;
231 goto out;
232 }
227 233
228 return chip; 234 return chip;
229 235
@@ -294,7 +300,6 @@ static int tpm_add_char_device(struct tpm_chip *chip)
294 "unable to cdev_add() %s, major %d, minor %d, err=%d\n", 300 "unable to cdev_add() %s, major %d, minor %d, err=%d\n",
295 dev_name(&chip->devs), MAJOR(chip->devs.devt), 301 dev_name(&chip->devs), MAJOR(chip->devs.devt),
296 MINOR(chip->devs.devt), rc); 302 MINOR(chip->devs.devt), rc);
297 tpm_del_char_device(chip, true);
298 return rc; 303 return rc;
299 } 304 }
300 305
@@ -306,7 +311,6 @@ static int tpm_add_char_device(struct tpm_chip *chip)
306 dev_name(&chip->devs), MAJOR(chip->devs.devt), 311 dev_name(&chip->devs), MAJOR(chip->devs.devt),
307 MINOR(chip->devs.devt), rc); 312 MINOR(chip->devs.devt), rc);
308 cdev_del(&chip->cdevs); 313 cdev_del(&chip->cdevs);
309 tpm_del_char_device(chip, true);
310 return rc; 314 return rc;
311 } 315 }
312 316
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 16dd207a4542..5eacb3fd2ed2 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -161,6 +161,8 @@ enum tpm2_cc_attrs {
161struct tpm_space { 161struct tpm_space {
162 u32 context_tbl[3]; 162 u32 context_tbl[3];
163 u8 *context_buf; 163 u8 *context_buf;
164 u32 session_tbl[3];
165 u8 *session_buf;
164}; 166};
165 167
166enum tpm_chip_flags { 168enum tpm_chip_flags {
@@ -588,7 +590,7 @@ unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal);
588int tpm2_probe(struct tpm_chip *chip); 590int tpm2_probe(struct tpm_chip *chip);
589int tpm2_find_cc(struct tpm_chip *chip, u32 cc); 591int tpm2_find_cc(struct tpm_chip *chip, u32 cc);
590int tpm2_init_space(struct tpm_space *space); 592int tpm2_init_space(struct tpm_space *space);
591void tpm2_del_space(struct tpm_space *space); 593void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space);
592int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u32 cc, 594int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u32 cc,
593 u8 *cmd); 595 u8 *cmd);
594int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space, 596int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space,
diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c
index e955548dbeed..e2e059d8ffec 100644
--- a/drivers/char/tpm/tpm2-space.c
+++ b/drivers/char/tpm/tpm2-space.c
@@ -32,18 +32,39 @@ struct tpm2_context {
32 __be16 blob_size; 32 __be16 blob_size;
33} __packed; 33} __packed;
34 34
35static 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_UNLOCKED);
43 }
44}
45
35int tpm2_init_space(struct tpm_space *space) 46int tpm2_init_space(struct tpm_space *space)
36{ 47{
37 space->context_buf = kzalloc(PAGE_SIZE, GFP_KERNEL); 48 space->context_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
38 if (!space->context_buf) 49 if (!space->context_buf)
39 return -ENOMEM; 50 return -ENOMEM;
40 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
41 return 0; 58 return 0;
42} 59}
43 60
44void tpm2_del_space(struct tpm_space *space) 61void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space)
45{ 62{
63 mutex_lock(&chip->tpm_mutex);
64 tpm2_flush_sessions(chip, space);
65 mutex_unlock(&chip->tpm_mutex);
46 kfree(space->context_buf); 66 kfree(space->context_buf);
67 kfree(space->session_buf);
47} 68}
48 69
49static int tpm2_load_context(struct tpm_chip *chip, u8 *buf, 70static int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
@@ -69,6 +90,20 @@ static int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
69 __func__, rc); 90 __func__, rc);
70 tpm_buf_destroy(&tbuf); 91 tpm_buf_destroy(&tbuf);
71 return -EFAULT; 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 rc = -ENOENT;
106 tpm_buf_destroy(&tbuf);
72 } else if (rc > 0) { 107 } else if (rc > 0) {
73 dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n", 108 dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
74 __func__, rc); 109 __func__, rc);
@@ -121,7 +156,6 @@ static int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf,
121 } 156 }
122 157
123 memcpy(&buf[*offset], &tbuf.data[TPM_HEADER_SIZE], body_size); 158 memcpy(&buf[*offset], &tbuf.data[TPM_HEADER_SIZE], body_size);
124 tpm2_flush_context_cmd(chip, handle, TPM_TRANSMIT_UNLOCKED);
125 *offset += body_size; 159 *offset += body_size;
126 tpm_buf_destroy(&tbuf); 160 tpm_buf_destroy(&tbuf);
127 return 0; 161 return 0;
@@ -136,6 +170,8 @@ static void tpm2_flush_space(struct tpm_chip *chip)
136 if (space->context_tbl[i] && ~space->context_tbl[i]) 170 if (space->context_tbl[i] && ~space->context_tbl[i])
137 tpm2_flush_context_cmd(chip, space->context_tbl[i], 171 tpm2_flush_context_cmd(chip, space->context_tbl[i],
138 TPM_TRANSMIT_UNLOCKED); 172 TPM_TRANSMIT_UNLOCKED);
173
174 tpm2_flush_sessions(chip, space);
139} 175}
140 176
141static int tpm2_load_space(struct tpm_chip *chip) 177static int tpm2_load_space(struct tpm_chip *chip)
@@ -161,6 +197,28 @@ static int tpm2_load_space(struct tpm_chip *chip)
161 return rc; 197 return rc;
162 } 198 }
163 199
200 for (i = 0, offset = 0; i < ARRAY_SIZE(space->session_tbl); i++) {
201 u32 handle;
202
203 if (!space->session_tbl[i])
204 continue;
205
206 rc = tpm2_load_context(chip, space->session_buf,
207 &offset, &handle);
208 if (rc == -ENOENT) {
209 /* load failed, just forget session */
210 space->session_tbl[i] = 0;
211 } else if (rc) {
212 tpm2_flush_space(chip);
213 return rc;
214 }
215 if (handle != space->session_tbl[i]) {
216 dev_warn(&chip->dev, "session restored to wrong handle\n");
217 tpm2_flush_space(chip);
218 return -EFAULT;
219 }
220 }
221
164 return 0; 222 return 0;
165} 223}
166 224
@@ -171,7 +229,7 @@ static bool tpm2_map_to_phandle(struct tpm_space *space, void *handle)
171 int i; 229 int i;
172 230
173 i = 0xFFFFFF - (vhandle & 0xFFFFFF); 231 i = 0xFFFFFF - (vhandle & 0xFFFFFF);
174 if (i > ARRAY_SIZE(space->context_tbl) || !space->context_tbl[i]) 232 if (i >= ARRAY_SIZE(space->context_tbl) || !space->context_tbl[i])
175 return false; 233 return false;
176 234
177 phandle = space->context_tbl[i]; 235 phandle = space->context_tbl[i];
@@ -215,7 +273,10 @@ int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u32 cc,
215 273
216 memcpy(&chip->work_space.context_tbl, &space->context_tbl, 274 memcpy(&chip->work_space.context_tbl, &space->context_tbl,
217 sizeof(space->context_tbl)); 275 sizeof(space->context_tbl));
276 memcpy(&chip->work_space.session_tbl, &space->session_tbl,
277 sizeof(space->session_tbl));
218 memcpy(chip->work_space.context_buf, space->context_buf, PAGE_SIZE); 278 memcpy(chip->work_space.context_buf, space->context_buf, PAGE_SIZE);
279 memcpy(chip->work_space.session_buf, space->session_buf, PAGE_SIZE);
219 280
220 rc = tpm2_load_space(chip); 281 rc = tpm2_load_space(chip);
221 if (rc) { 282 if (rc) {
@@ -232,6 +293,22 @@ int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u32 cc,
232 return 0; 293 return 0;
233} 294}
234 295
296static bool tpm2_add_session(struct tpm_chip *chip, u32 handle)
297{
298 struct tpm_space *space = &chip->work_space;
299 int i;
300
301 for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++)
302 if (space->session_tbl[i] == 0)
303 break;
304
305 if (i == ARRAY_SIZE(space->session_tbl))
306 return false;
307
308 space->session_tbl[i] = handle;
309 return true;
310}
311
235static u32 tpm2_map_to_vhandle(struct tpm_space *space, u32 phandle, bool alloc) 312static u32 tpm2_map_to_vhandle(struct tpm_space *space, u32 phandle, bool alloc)
236{ 313{
237 int i; 314 int i;
@@ -288,6 +365,8 @@ static int tpm2_map_response_header(struct tpm_chip *chip, u32 cc, u8 *rsp,
288 break; 365 break;
289 case TPM2_HT_HMAC_SESSION: 366 case TPM2_HT_HMAC_SESSION:
290 case TPM2_HT_POLICY_SESSION: 367 case TPM2_HT_POLICY_SESSION:
368 if (!tpm2_add_session(chip, phandle))
369 goto out_no_slots;
291 break; 370 break;
292 default: 371 default:
293 dev_err(&chip->dev, "%s: unknown handle 0x%08X\n", 372 dev_err(&chip->dev, "%s: unknown handle 0x%08X\n",
@@ -350,15 +429,11 @@ static int tpm2_map_response_body(struct tpm_chip *chip, u32 cc, u8 *rsp,
350 data->handles[j] = cpu_to_be32(vhandle); 429 data->handles[j] = cpu_to_be32(vhandle);
351 j++; 430 j++;
352 break; 431 break;
353 case TPM2_HT_HMAC_SESSION: 432
354 case TPM2_HT_POLICY_SESSION: 433 default:
355 data->handles[j] = cpu_to_be32(phandle); 434 data->handles[j] = cpu_to_be32(phandle);
356 j++; 435 j++;
357 break; 436 break;
358 default:
359 dev_err(&chip->dev, "%s: unknown handle 0x%08X\n",
360 __func__, phandle);
361 break;
362 } 437 }
363 438
364 } 439 }
@@ -388,9 +463,28 @@ static int tpm2_save_space(struct tpm_chip *chip)
388 } else if (rc) 463 } else if (rc)
389 return rc; 464 return rc;
390 465
466 tpm2_flush_context_cmd(chip, space->context_tbl[i],
467 TPM_TRANSMIT_UNLOCKED);
391 space->context_tbl[i] = ~0; 468 space->context_tbl[i] = ~0;
392 } 469 }
393 470
471 for (i = 0, offset = 0; i < ARRAY_SIZE(space->session_tbl); i++) {
472 if (!space->session_tbl[i])
473 continue;
474
475 rc = tpm2_save_context(chip, space->session_tbl[i],
476 space->session_buf, PAGE_SIZE,
477 &offset);
478
479 if (rc == -ENOENT) {
480 /* handle error saving session, just forget it */
481 space->session_tbl[i] = 0;
482 } else if (rc < 0) {
483 tpm2_flush_space(chip);
484 return rc;
485 }
486 }
487
394 return 0; 488 return 0;
395} 489}
396 490
@@ -425,7 +519,10 @@ int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space,
425 519
426 memcpy(&space->context_tbl, &chip->work_space.context_tbl, 520 memcpy(&space->context_tbl, &chip->work_space.context_tbl,
427 sizeof(space->context_tbl)); 521 sizeof(space->context_tbl));
522 memcpy(&space->session_tbl, &chip->work_space.session_tbl,
523 sizeof(space->session_tbl));
428 memcpy(space->context_buf, chip->work_space.context_buf, PAGE_SIZE); 524 memcpy(space->context_buf, chip->work_space.context_buf, PAGE_SIZE);
525 memcpy(space->session_buf, chip->work_space.session_buf, PAGE_SIZE);
429 526
430 return 0; 527 return 0;
431} 528}
diff --git a/drivers/char/tpm/tpmrm-dev.c b/drivers/char/tpm/tpmrm-dev.c
index 630bddce65a8..c636e7fdd1f5 100644
--- a/drivers/char/tpm/tpmrm-dev.c
+++ b/drivers/char/tpm/tpmrm-dev.c
@@ -39,7 +39,7 @@ static int tpmrm_release(struct inode *inode, struct file *file)
39 struct tpmrm_priv *priv = container_of(fpriv, struct tpmrm_priv, priv); 39 struct tpmrm_priv *priv = container_of(fpriv, struct tpmrm_priv, priv);
40 40
41 tpm_common_release(file, fpriv); 41 tpm_common_release(file, fpriv);
42 tpm2_del_space(&priv->space); 42 tpm2_del_space(fpriv->chip, &priv->space);
43 kfree(priv); 43 kfree(priv);
44 44
45 return 0; 45 return 0;