]> Git Repo - J-linux.git/blob - drivers/char/tpm/tpm2-space.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / drivers / char / tpm / tpm2-space.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2016 Intel Corporation
4  *
5  * Authors:
6  * Jarkko Sakkinen <[email protected]>
7  *
8  * Maintained by: <[email protected]>
9  *
10  * This file contains TPM2 protocol implementations of the commands
11  * used by the kernel internally.
12  */
13
14 #include <linux/gfp.h>
15 #include <linux/unaligned.h>
16 #include "tpm.h"
17
18 enum tpm2_handle_types {
19         TPM2_HT_HMAC_SESSION    = 0x02000000,
20         TPM2_HT_POLICY_SESSION  = 0x03000000,
21         TPM2_HT_TRANSIENT       = 0x80000000,
22 };
23
24 struct tpm2_context {
25         __be64 sequence;
26         __be32 saved_handle;
27         __be32 hierarchy;
28         __be16 blob_size;
29 } __packed;
30
31 static 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
41 int 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
59 void 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
71 int 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'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 (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
125 int 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
167 void 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
182 static 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
230 static 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
245 static 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
271 static 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;
300 err_len:
301         dev_dbg(&chip->dev, "%s: insufficient command length %zu", __func__,
302                 len);
303         return -EINVAL;
304 }
305
306 int 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
344 static 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
360 static 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
380 static 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;
426 out_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
433 struct tpm2_cap_handles {
434         u8 more_data;
435         __be32 capability;
436         __be32 count;
437         __be32 handles[];
438 } __packed;
439
440 static 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
497 static 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
540 int 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;
579 out:
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  */
587 static 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 device
597  * reference. This may also release the reference to the master device.
598  */
599 void 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 the
607  * main device.
608  */
609 int 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. The
620          * 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
643 err_put_devs:
644         put_device(&chip->devs);
645
646         return rc;
647 }
This page took 0.068158 seconds and 4 git commands to generate.