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