xref: /qemu/backends/tpm/tpm_passthrough.c (revision 0d09e41a51aa0752b1ce525ce084f7cd210e461b)
1 /*
2  *  passthrough TPM driver
3  *
4  *  Copyright (c) 2010 - 2013 IBM Corporation
5  *  Authors:
6  *    Stefan Berger <stefanb@us.ibm.com>
7  *
8  *  Copyright (C) 2011 IAIK, Graz University of Technology
9  *    Author: Andreas Niederl
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, see <http://www.gnu.org/licenses/>
23  */
24 
25 #include <dirent.h>
26 
27 #include "qemu-common.h"
28 #include "qapi/error.h"
29 #include "qemu/sockets.h"
30 #include "backends/tpm.h"
31 #include "tpm_int.h"
32 #include "hw/hw.h"
33 #include "hw/i386/pc.h"
34 #include "tpm_tis.h"
35 #include "tpm_backend.h"
36 
37 /* #define DEBUG_TPM */
38 
39 #ifdef DEBUG_TPM
40 #define DPRINTF(fmt, ...) \
41     do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
42 #else
43 #define DPRINTF(fmt, ...) \
44     do { } while (0)
45 #endif
46 
47 #define TYPE_TPM_PASSTHROUGH "tpm-passthrough"
48 #define TPM_PASSTHROUGH(obj) \
49     OBJECT_CHECK(TPMPassthruState, (obj), TYPE_TPM_PASSTHROUGH)
50 
51 /* data structures */
52 typedef struct TPMPassthruThreadParams {
53     TPMState *tpm_state;
54 
55     TPMRecvDataCB *recv_data_callback;
56     TPMBackend *tb;
57 } TPMPassthruThreadParams;
58 
59 struct TPMPassthruState {
60     TPMBackend parent;
61 
62     TPMBackendThread tbt;
63 
64     TPMPassthruThreadParams tpm_thread_params;
65 
66     char *tpm_dev;
67     int tpm_fd;
68     bool tpm_executing;
69     bool tpm_op_canceled;
70     int cancel_fd;
71     bool had_startup_error;
72 };
73 
74 typedef struct TPMPassthruState TPMPassthruState;
75 
76 #define TPM_PASSTHROUGH_DEFAULT_DEVICE "/dev/tpm0"
77 
78 /* functions */
79 
80 static void tpm_passthrough_cancel_cmd(TPMBackend *tb);
81 
82 static int tpm_passthrough_unix_write(int fd, const uint8_t *buf, uint32_t len)
83 {
84     return send_all(fd, buf, len);
85 }
86 
87 static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len)
88 {
89     return recv_all(fd, buf, len, true);
90 }
91 
92 static uint32_t tpm_passthrough_get_size_from_buffer(const uint8_t *buf)
93 {
94     struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)buf;
95 
96     return be32_to_cpu(resp->len);
97 }
98 
99 static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
100                                         const uint8_t *in, uint32_t in_len,
101                                         uint8_t *out, uint32_t out_len)
102 {
103     int ret;
104 
105     tpm_pt->tpm_op_canceled = false;
106     tpm_pt->tpm_executing = true;
107 
108     ret = tpm_passthrough_unix_write(tpm_pt->tpm_fd, in, in_len);
109     if (ret != in_len) {
110         if (!tpm_pt->tpm_op_canceled ||
111             (tpm_pt->tpm_op_canceled && errno != ECANCELED)) {
112             error_report("tpm_passthrough: error while transmitting data "
113                          "to TPM: %s (%i)\n",
114                          strerror(errno), errno);
115         }
116         goto err_exit;
117     }
118 
119     tpm_pt->tpm_executing = false;
120 
121     ret = tpm_passthrough_unix_read(tpm_pt->tpm_fd, out, out_len);
122     if (ret < 0) {
123         if (!tpm_pt->tpm_op_canceled ||
124             (tpm_pt->tpm_op_canceled && errno != ECANCELED)) {
125             error_report("tpm_passthrough: error while reading data from "
126                          "TPM: %s (%i)\n",
127                          strerror(errno), errno);
128         }
129     } else if (ret < sizeof(struct tpm_resp_hdr) ||
130                tpm_passthrough_get_size_from_buffer(out) != ret) {
131         ret = -1;
132         error_report("tpm_passthrough: received invalid response "
133                      "packet from TPM\n");
134     }
135 
136 err_exit:
137     if (ret < 0) {
138         tpm_write_fatal_error_response(out, out_len);
139     }
140 
141     tpm_pt->tpm_executing = false;
142 
143     return ret;
144 }
145 
146 static int tpm_passthrough_unix_transfer(TPMPassthruState *tpm_pt,
147                                          const TPMLocality *locty_data)
148 {
149     return tpm_passthrough_unix_tx_bufs(tpm_pt,
150                                         locty_data->w_buffer.buffer,
151                                         locty_data->w_offset,
152                                         locty_data->r_buffer.buffer,
153                                         locty_data->r_buffer.size);
154 }
155 
156 static void tpm_passthrough_worker_thread(gpointer data,
157                                           gpointer user_data)
158 {
159     TPMPassthruThreadParams *thr_parms = user_data;
160     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(thr_parms->tb);
161     TPMBackendCmd cmd = (TPMBackendCmd)data;
162 
163     DPRINTF("tpm_passthrough: processing command type %d\n", cmd);
164 
165     switch (cmd) {
166     case TPM_BACKEND_CMD_PROCESS_CMD:
167         tpm_passthrough_unix_transfer(tpm_pt,
168                                       thr_parms->tpm_state->locty_data);
169 
170         thr_parms->recv_data_callback(thr_parms->tpm_state,
171                                       thr_parms->tpm_state->locty_number);
172         break;
173     case TPM_BACKEND_CMD_INIT:
174     case TPM_BACKEND_CMD_END:
175     case TPM_BACKEND_CMD_TPM_RESET:
176         /* nothing to do */
177         break;
178     }
179 }
180 
181 /*
182  * Start the TPM (thread). If it had been started before, then terminate
183  * and start it again.
184  */
185 static int tpm_passthrough_startup_tpm(TPMBackend *tb)
186 {
187     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
188 
189     /* terminate a running TPM */
190     tpm_backend_thread_end(&tpm_pt->tbt);
191 
192     tpm_backend_thread_create(&tpm_pt->tbt,
193                               tpm_passthrough_worker_thread,
194                               &tpm_pt->tpm_thread_params);
195 
196     return 0;
197 }
198 
199 static void tpm_passthrough_reset(TPMBackend *tb)
200 {
201     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
202 
203     DPRINTF("tpm_passthrough: CALL TO TPM_RESET!\n");
204 
205     tpm_passthrough_cancel_cmd(tb);
206 
207     tpm_backend_thread_end(&tpm_pt->tbt);
208 
209     tpm_pt->had_startup_error = false;
210 }
211 
212 static int tpm_passthrough_init(TPMBackend *tb, TPMState *s,
213                                 TPMRecvDataCB *recv_data_cb)
214 {
215     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
216 
217     tpm_pt->tpm_thread_params.tpm_state = s;
218     tpm_pt->tpm_thread_params.recv_data_callback = recv_data_cb;
219     tpm_pt->tpm_thread_params.tb = tb;
220 
221     return 0;
222 }
223 
224 static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb)
225 {
226     return false;
227 }
228 
229 static bool tpm_passthrough_get_startup_error(TPMBackend *tb)
230 {
231     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
232 
233     return tpm_pt->had_startup_error;
234 }
235 
236 static size_t tpm_passthrough_realloc_buffer(TPMSizedBuffer *sb)
237 {
238     size_t wanted_size = 4096; /* Linux tpm.c buffer size */
239 
240     if (sb->size != wanted_size) {
241         sb->buffer = g_realloc(sb->buffer, wanted_size);
242         sb->size = wanted_size;
243     }
244     return sb->size;
245 }
246 
247 static void tpm_passthrough_deliver_request(TPMBackend *tb)
248 {
249     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
250 
251     tpm_backend_thread_deliver_request(&tpm_pt->tbt);
252 }
253 
254 static void tpm_passthrough_cancel_cmd(TPMBackend *tb)
255 {
256     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
257     int n;
258 
259     /*
260      * As of Linux 3.7 the tpm_tis driver does not properly cancel
261      * commands on all TPM manufacturers' TPMs.
262      * Only cancel if we're busy so we don't cancel someone else's
263      * command, e.g., a command executed on the host.
264      */
265     if (tpm_pt->tpm_executing) {
266         if (tpm_pt->cancel_fd >= 0) {
267             n = write(tpm_pt->cancel_fd, "-", 1);
268             if (n != 1) {
269                 error_report("Canceling TPM command failed: %s\n",
270                              strerror(errno));
271             } else {
272                 tpm_pt->tpm_op_canceled = true;
273             }
274         } else {
275             error_report("Cannot cancel TPM command due to missing "
276                          "TPM sysfs cancel entry");
277         }
278     }
279 }
280 
281 static const char *tpm_passthrough_create_desc(void)
282 {
283     return "Passthrough TPM backend driver";
284 }
285 
286 /*
287  * A basic test of a TPM device. We expect a well formatted response header
288  * (error response is fine) within one second.
289  */
290 static int tpm_passthrough_test_tpmdev(int fd)
291 {
292     struct tpm_req_hdr req = {
293         .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND),
294         .len = cpu_to_be32(sizeof(req)),
295         .ordinal = cpu_to_be32(TPM_ORD_GetTicks),
296     };
297     struct tpm_resp_hdr *resp;
298     fd_set readfds;
299     int n;
300     struct timeval tv = {
301         .tv_sec = 1,
302         .tv_usec = 0,
303     };
304     unsigned char buf[1024];
305 
306     n = write(fd, &req, sizeof(req));
307     if (n < 0) {
308         return errno;
309     }
310     if (n != sizeof(req)) {
311         return EFAULT;
312     }
313 
314     FD_ZERO(&readfds);
315     FD_SET(fd, &readfds);
316 
317     /* wait for a second */
318     n = select(fd + 1, &readfds, NULL, NULL, &tv);
319     if (n != 1) {
320         return errno;
321     }
322 
323     n = read(fd, &buf, sizeof(buf));
324     if (n < sizeof(struct tpm_resp_hdr)) {
325         return EFAULT;
326     }
327 
328     resp = (struct tpm_resp_hdr *)buf;
329     /* check the header */
330     if (be16_to_cpu(resp->tag) != TPM_TAG_RSP_COMMAND ||
331         be32_to_cpu(resp->len) != n) {
332         return EBADMSG;
333     }
334 
335     return 0;
336 }
337 
338 /*
339  * Check whether the given base path, e.g.,  /sys/class/misc/tpm0/device,
340  * is the sysfs directory of a TPM. A TPM sysfs directory should be uniquely
341  * recognizable by the file entries 'pcrs' and 'cancel'.
342  * Upon success 'true' is returned and the basebath buffer has '/cancel'
343  * appended.
344  */
345 static bool tpm_passthrough_check_sysfs_cancel(char *basepath, size_t bufsz)
346 {
347     char path[PATH_MAX];
348     struct stat statbuf;
349 
350     snprintf(path, sizeof(path), "%s/pcrs", basepath);
351     if (stat(path, &statbuf) == -1 || !S_ISREG(statbuf.st_mode)) {
352         return false;
353     }
354 
355     snprintf(path, sizeof(path), "%s/cancel", basepath);
356     if (stat(path, &statbuf) == -1 || !S_ISREG(statbuf.st_mode)) {
357         return false;
358     }
359 
360     strncpy(basepath, path, bufsz);
361 
362     return true;
363 }
364 
365 /*
366  * Unless path or file descriptor set has been provided by user,
367  * determine the sysfs cancel file following kernel documentation
368  * in Documentation/ABI/stable/sysfs-class-tpm.
369  */
370 static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb)
371 {
372     int fd = -1;
373     unsigned int idx;
374     DIR *pnp_dir;
375     char path[PATH_MAX];
376     struct dirent entry, *result;
377     int len;
378 
379     if (tb->cancel_path) {
380         fd = qemu_open(tb->cancel_path, O_WRONLY);
381         if (fd < 0) {
382             error_report("Could not open TPM cancel path : %s",
383                          strerror(errno));
384         }
385         return fd;
386     }
387 
388     snprintf(path, sizeof(path), "/sys/class/misc");
389     pnp_dir = opendir(path);
390     if (pnp_dir != NULL) {
391         while (readdir_r(pnp_dir, &entry, &result) == 0 &&
392                result != NULL) {
393             /*
394              * only allow /sys/class/misc/tpm%u type of paths
395              */
396             if (sscanf(entry.d_name, "tpm%u%n", &idx, &len) < 1 ||
397                 len <= strlen("tpm") ||
398                 len != strlen(entry.d_name)) {
399                 continue;
400             }
401 
402             snprintf(path, sizeof(path), "/sys/class/misc/%s/device",
403                      entry.d_name);
404             if (!tpm_passthrough_check_sysfs_cancel(path, sizeof(path))) {
405                 continue;
406             }
407 
408             fd = qemu_open(path, O_WRONLY);
409             break;
410         }
411         closedir(pnp_dir);
412     }
413 
414     if (fd >= 0) {
415         tb->cancel_path = g_strdup(path);
416     }
417 
418     return fd;
419 }
420 
421 static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
422 {
423     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
424     const char *value;
425 
426     value = qemu_opt_get(opts, "cancel-path");
427     if (value) {
428         tb->cancel_path = g_strdup(value);
429     }
430 
431     value = qemu_opt_get(opts, "path");
432     if (!value) {
433         value = TPM_PASSTHROUGH_DEFAULT_DEVICE;
434     }
435 
436     tpm_pt->tpm_dev = g_strdup(value);
437 
438     tb->path = g_strdup(tpm_pt->tpm_dev);
439 
440     tpm_pt->tpm_fd = qemu_open(tpm_pt->tpm_dev, O_RDWR);
441     if (tpm_pt->tpm_fd < 0) {
442         error_report("Cannot access TPM device using '%s': %s\n",
443                      tpm_pt->tpm_dev, strerror(errno));
444         goto err_free_parameters;
445     }
446 
447     if (tpm_passthrough_test_tpmdev(tpm_pt->tpm_fd)) {
448         error_report("'%s' is not a TPM device.\n",
449                      tpm_pt->tpm_dev);
450         goto err_close_tpmdev;
451     }
452 
453     return 0;
454 
455  err_close_tpmdev:
456     qemu_close(tpm_pt->tpm_fd);
457     tpm_pt->tpm_fd = -1;
458 
459  err_free_parameters:
460     g_free(tb->path);
461     tb->path = NULL;
462 
463     g_free(tpm_pt->tpm_dev);
464     tpm_pt->tpm_dev = NULL;
465 
466     return 1;
467 }
468 
469 static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id)
470 {
471     Object *obj = object_new(TYPE_TPM_PASSTHROUGH);
472     TPMBackend *tb = TPM_BACKEND(obj);
473     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
474 
475     tb->id = g_strdup(id);
476     /* let frontend set the fe_model to proper value */
477     tb->fe_model = -1;
478 
479     tb->ops = &tpm_passthrough_driver;
480 
481     if (tpm_passthrough_handle_device_opts(opts, tb)) {
482         goto err_exit;
483     }
484 
485     tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tb);
486     if (tpm_pt->cancel_fd < 0) {
487         goto err_exit;
488     }
489 
490     return tb;
491 
492 err_exit:
493     g_free(tb->id);
494 
495     return NULL;
496 }
497 
498 static void tpm_passthrough_destroy(TPMBackend *tb)
499 {
500     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
501 
502     tpm_passthrough_cancel_cmd(tb);
503 
504     tpm_backend_thread_end(&tpm_pt->tbt);
505 
506     qemu_close(tpm_pt->tpm_fd);
507     qemu_close(tpm_pt->cancel_fd);
508 
509     g_free(tb->id);
510     g_free(tb->path);
511     g_free(tb->cancel_path);
512     g_free(tpm_pt->tpm_dev);
513 }
514 
515 const TPMDriverOps tpm_passthrough_driver = {
516     .type                     = TPM_TYPE_PASSTHROUGH,
517     .desc                     = tpm_passthrough_create_desc,
518     .create                   = tpm_passthrough_create,
519     .destroy                  = tpm_passthrough_destroy,
520     .init                     = tpm_passthrough_init,
521     .startup_tpm              = tpm_passthrough_startup_tpm,
522     .realloc_buffer           = tpm_passthrough_realloc_buffer,
523     .reset                    = tpm_passthrough_reset,
524     .had_startup_error        = tpm_passthrough_get_startup_error,
525     .deliver_request          = tpm_passthrough_deliver_request,
526     .cancel_cmd               = tpm_passthrough_cancel_cmd,
527     .get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag,
528 };
529 
530 static void tpm_passthrough_inst_init(Object *obj)
531 {
532 }
533 
534 static void tpm_passthrough_inst_finalize(Object *obj)
535 {
536 }
537 
538 static void tpm_passthrough_class_init(ObjectClass *klass, void *data)
539 {
540     TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass);
541 
542     tbc->ops = &tpm_passthrough_driver;
543 }
544 
545 static const TypeInfo tpm_passthrough_info = {
546     .name = TYPE_TPM_PASSTHROUGH,
547     .parent = TYPE_TPM_BACKEND,
548     .instance_size = sizeof(TPMPassthruState),
549     .class_init = tpm_passthrough_class_init,
550     .instance_init = tpm_passthrough_inst_init,
551     .instance_finalize = tpm_passthrough_inst_finalize,
552 };
553 
554 static void tpm_passthrough_register(void)
555 {
556     type_register_static(&tpm_passthrough_info);
557     tpm_register_driver(&tpm_passthrough_driver);
558 }
559 
560 type_init(tpm_passthrough_register)
561