]> git.proxmox.com Git - swtpm.git/blob - src/swtpm/cuse_tpm.c
Add ioctl to get configuration flags about keys in use
[swtpm.git] / src / swtpm / cuse_tpm.c
1 /*
2 * ptm - CUSE based TPM PassThrough Multiplexer for QEMU.
3 *
4 * This program instantiates one /dev/vtpm* device, and
5 * calls libtpms to handle requests
6 *
7 * The following code was derived from
8 * http://fuse.sourceforge.net/doxygen/cusexmp_8c.html
9 *
10 * It's original header states:
11 *
12 * CUSE example: Character device in Userspace
13 * Copyright (C) 2008-2009 SUSE Linux Products GmbH
14 * Copyright (C) 2008-2009 Tejun Heo <tj@kernel.org>
15 * This program can be distributed under the terms of the GNU GPL.
16 * See the file COPYING.
17 *
18 *
19 * Authors: David Safford safford@us.ibm.com
20 * Stefan Berger stefanb@us.ibm.com
21 *
22 */
23
24 /*
25 * Note: It's possible for multiple process to open access to
26 * the same character device. Concurrency problems may arise
27 * if those processes all write() to the device and then try
28 * to pick up the results. Proper usage of the device is to
29 * have one process (QEMU) use ioctl, read and write and have
30 * other processes (libvirt, etc.) only use ioctl.
31 */
32 #define FUSE_USE_VERSION 29
33
34 #include <stddef.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <dirent.h>
40 #include <errno.h>
41 #include <stdbool.h>
42 #include <sys/types.h>
43 #include <ctype.h>
44 #include <pwd.h>
45 #include <grp.h>
46 #include <arpa/inet.h>
47
48 #include <libtpms/tpm_library.h>
49 #include <libtpms/tpm_tis.h>
50 #include <libtpms/tpm_error.h>
51 #include <libtpms/tpm_memory.h>
52 #include <libtpms/tpm_nvfilename.h>
53
54 #include "cuse_lowlevel.h"
55 #include "fuse_opt.h"
56 #include "tpm_ioctl.h"
57 #include "swtpm.h"
58 #include "swtpm_nvfile.h"
59 #include "key.h"
60 #include "logging.h"
61 #include "main.h"
62 #include "common.h"
63
64 #include <glib.h>
65
66 #define TPM_REQ_MAX 4096
67 static unsigned char *ptm_req, *ptm_res;
68 static uint32_t ptm_req_len, ptm_res_len, ptm_res_tot;
69 static TPM_MODIFIER_INDICATOR locality;
70 static int tpm_running;
71 static int thread_busy;
72 static GThreadPool *pool;
73 static struct passwd *passwd;
74
75 #if GLIB_MAJOR_VERSION >= 2
76 # if GLIB_MINOR_VERSION >= 32
77
78 GCond thread_busy_signal;
79 GMutex thread_busy_lock;
80 GMutex file_ops_lock;
81 # define THREAD_BUSY_SIGNAL &thread_busy_signal
82 # define THREAD_BUSY_LOCK &thread_busy_lock
83 # define FILE_OPS_LOCK &file_ops_lock
84
85 # else
86
87 GCond *thread_busy_signal;
88 GMutex *thread_busy_lock;
89 GMutex *file_ops_lock;
90 # define THREAD_BUSY_SIGNAL thread_busy_signal
91 # define THREAD_BUSY_LOCK thread_busy_lock
92 # define FILE_OPS_LOCK file_ops_lock
93
94 # endif
95 #else
96
97 #error Unsupport glib version
98
99 #endif
100
101 struct ptm_param {
102 unsigned major;
103 unsigned minor;
104 char *dev_name;
105 int is_help;
106 const char *prgname;
107 char *runas;
108 char *logging;
109 char *keydata;
110 char *migkeydata;
111 };
112
113
114 enum msg_type {
115 MESSAGE_TPM_CMD = 1,
116 MESSAGE_IOCTL,
117 };
118
119 struct thread_message {
120 enum msg_type type;
121 fuse_req_t req;
122 };
123
124 #define min(a,b) ((a) < (b) ? (a) : (b))
125
126 struct stateblob {
127 uint8_t type;
128 uint8_t *data;
129 uint32_t length;
130 };
131
132 static const char *usage =
133 "usage: %s [options]\n"
134 "\n"
135 "The following options are supported:\n"
136 "\n"
137 "-n NAME|--name=NAME : device name (mandatory)\n"
138 "-M MAJ|--maj=MAJ : device major number\n"
139 "-m MIN|--min=MIN : device minor number\n"
140 "--key file=<path>[,mode=aes-cbc][,format=hex|binary][,remove=[true|false]]\n"
141 " : use an AES key for the encryption of the TPM's state\n"
142 " files; use the given mode for the block encryption;\n"
143 " the key is to be provided as a hex string or in binary\n"
144 " format; the keyfile can be automatically removed using\n"
145 " the remove parameter\n"
146 "--key pwdfile=<path>[,mode=aes-cbc][,remove=[true|false]]\n"
147 " : provide a passphrase in a file; the AES key will be\n"
148 " derived from this passphrase\n"
149 "--migration-key file=<path>,[,mode=aes-cbc][,format=hex|binary][,remove=[true|false]]\n"
150 " : use an AES key for the encryption of the TPM's state\n"
151 " when it is retrieved from the TPM via ioctls;\n"
152 " Setting this key ensures that the TPM's state will always\n"
153 " be encrypted when migrated\n"
154 "--migration-key pwdfile=<path>[,mode=aes-cbc][,remove=[true|false]]\n"
155 " : provide a passphrase in a file; the AES key will be\n"
156 " derived from this passphrase\n"
157 "--log file=<path>|fd=<filedescriptor>\n"
158 " : write the TPM's log into the given file rather than\n"
159 " to the console; provide '-' for path to avoid logging\n"
160 "-h|--help : display this help screen and terminate\n"
161 "\n"
162 "Make sure that TPM_PATH environment variable points to directory\n"
163 "where TPM's NV storage file is kept\n"
164 "\n";
165
166 const static unsigned char TPM_Resp_FatalError[] = {
167 0x00, 0xC4, /* TPM Response */
168 0x00, 0x00, 0x00, 0x0A, /* length (10) */
169 0x00, 0x00, 0x00, 0x09 /* TPM_FAIL */
170 };
171
172 const static unsigned char TPM_ResetEstablishmentBit[] = {
173 0x00, 0xC1, /* TPM Request */
174 0x00, 0x00, 0x00, 0x0A, /* length (10) */
175 0x40, 0x00, 0x00, 0x0B /* TPM_ORD_ResetEstablishmentBit */
176 };
177
178 typedef struct TPM_Response_Header {
179 uint16_t tag;
180 uint32_t paramSize;
181 uint32_t returnCode;
182 } __attribute__ ((packed)) TPM_Response_Header;
183
184 static TPM_RESULT
185 ptm_io_getlocality(TPM_MODIFIER_INDICATOR *loc, uint32_t tpmnum)
186 {
187 *loc = locality;
188 return TPM_SUCCESS;
189 }
190
191 static struct libtpms_callbacks cbs = {
192 .sizeOfStruct = sizeof(struct libtpms_callbacks),
193 .tpm_nvram_init = SWTPM_NVRAM_Init,
194 .tpm_nvram_loaddata = SWTPM_NVRAM_LoadData,
195 .tpm_nvram_storedata = SWTPM_NVRAM_StoreData,
196 .tpm_nvram_deletename = SWTPM_NVRAM_DeleteName,
197 .tpm_io_getlocality = ptm_io_getlocality,
198 };
199
200 static struct thread_message msg;
201
202 /* worker_thread_wait_done
203 *
204 * Wait while the TPM worker thread is busy
205 */
206 static void worker_thread_wait_done(void)
207 {
208 g_mutex_lock(THREAD_BUSY_LOCK);
209 while (thread_busy) {
210 #if GLIB_MINOR_VERSION >= 32
211 gint64 end_time = g_get_monotonic_time() +
212 1 * G_TIME_SPAN_SECOND;
213 g_cond_wait_until(THREAD_BUSY_SIGNAL,
214 THREAD_BUSY_LOCK,
215 end_time);
216 #else
217 GTimeVal abs_time;
218 /*
219 * seems like occasionally the g_cond_signal did not wake up
220 * the sleeping task; so we poll [TIS Test in BIOS]
221 */
222 abs_time.tv_sec = 1;
223 abs_time.tv_usec = 0;
224 g_cond_timed_wait(THREAD_BUSY_SIGNAL,
225 THREAD_BUSY_LOCK,
226 &abs_time);
227 #endif
228 }
229 g_mutex_unlock(THREAD_BUSY_LOCK);
230 }
231
232 /* worker_thread_mark_busy
233 *
234 * Mark the worker thread as busy; call this with the lock held
235 */
236 static void worker_thread_mark_busy(void)
237 {
238 g_mutex_lock(THREAD_BUSY_LOCK);
239 thread_busy = 1;
240 g_mutex_unlock(THREAD_BUSY_LOCK);
241 }
242
243 /* work_tread_mark_done
244 *
245 * Mark the worker thread as done and wake
246 * up the waiting thread
247 */
248 static void worker_thread_mark_done(void)
249 {
250 g_mutex_lock(THREAD_BUSY_LOCK);
251 thread_busy = 0;
252 g_cond_signal(THREAD_BUSY_SIGNAL);
253 g_mutex_unlock(THREAD_BUSY_LOCK);
254 }
255
256 /* worker_thread_is_busy
257 *
258 * Determine whether the worker thread is busy
259 */
260 static int worker_thread_is_busy()
261 {
262 return thread_busy;
263 }
264
265 static void worker_thread(gpointer data, gpointer user_data)
266 {
267 struct thread_message *msg = (struct thread_message *)data;
268
269 switch (msg->type) {
270 case MESSAGE_TPM_CMD:
271 TPMLIB_Process(&ptm_res, &ptm_res_len, &ptm_res_tot,
272 ptm_req, ptm_req_len);
273 break;
274 case MESSAGE_IOCTL:
275 break;
276 }
277
278 /* results are ready */
279 worker_thread_mark_done();
280 }
281
282 /* worker_thread_end
283 *
284 * finish the worker thread
285 */
286 static void worker_thread_end()
287 {
288 if (pool) {
289 worker_thread_wait_done();
290 g_thread_pool_free(pool, TRUE, TRUE);
291 pool = NULL;
292 }
293 }
294
295 /* _TPM_IO_TpmEstablished_Reset
296 *
297 * Reset the TPM Established bit
298 */
299 static TPM_RESULT
300 _TPM_IO_TpmEstablished_Reset(fuse_req_t req,
301 TPM_MODIFIER_INDICATOR locty)
302 {
303 TPM_RESULT res = TPM_FAIL;
304 TPM_Response_Header *tpmrh;
305 TPM_MODIFIER_INDICATOR orig_locality = locality;
306
307 locality = locty;
308
309 ptm_req_len = sizeof(TPM_ResetEstablishmentBit);
310 memcpy(ptm_req, TPM_ResetEstablishmentBit, ptm_req_len);
311 msg.type = MESSAGE_TPM_CMD;
312 msg.req = req;
313
314 worker_thread_mark_busy();
315
316 g_thread_pool_push(pool, &msg, NULL);
317
318 worker_thread_wait_done();
319
320 if (ptm_res_len >= sizeof(TPM_Response_Header)) {
321 tpmrh = (TPM_Response_Header *)ptm_res;
322 res = ntohl(tpmrh->returnCode);
323 }
324
325 locality = orig_locality;
326
327 return res;
328 }
329
330 static int tpm_start(uint32_t flags)
331 {
332 DIR *dir;
333 char * tpmdir = NULL;
334
335 /* temporary - the backend script lacks the perms to do this */
336 if (tpmdir == NULL) {
337 tpmdir = getenv("TPM_PATH");
338 if (!tpmdir) {
339 logprintf(STDOUT_FILENO,
340 "Error: TPM_PATH is not set\n");
341 return -1;
342 }
343 }
344 dir = opendir(tpmdir);
345 if (dir) {
346 closedir(dir);
347 } else {
348 if (mkdir(tpmdir, 0775)) {
349 logprintf(STDERR_FILENO,
350 "Error: Could not open TPM_PATH dir\n");
351 return -1;
352 }
353 }
354
355 pool = g_thread_pool_new(worker_thread,
356 NULL,
357 1,
358 TRUE,
359 NULL);
360 if (!pool) {
361 logprintf(STDERR_FILENO,
362 "Error: Could not create the thread pool.\n");
363 return -1;
364 }
365
366 if (TPMLIB_RegisterCallbacks(&cbs) != TPM_SUCCESS) {
367 logprintf(STDERR_FILENO,
368 "Error: Could not register the callbacks.\n");
369 goto error_del_pool;
370 }
371
372 if (TPMLIB_MainInit() != TPM_SUCCESS) {
373 logprintf(STDERR_FILENO,
374 "Error: Could not start the CUSE TPM.\n");
375 goto error_del_pool;
376 }
377
378 if (flags & INIT_FLAG_DELETE_VOLATILE) {
379 uint32_t tpm_number = 0;
380 char *name = TPM_VOLATILESTATE_NAME;
381 if (SWTPM_NVRAM_DeleteName(tpm_number,
382 name,
383 FALSE) != TPM_SUCCESS) {
384 logprintf(STDERR_FILENO,
385 "Error: Could not delete the volatile "
386 "state of the TPM.\n");
387 goto error_terminate;
388 }
389 }
390
391 if(!ptm_req)
392 ptm_req = malloc(4096);
393 if(!ptm_req) {
394 logprintf(STDERR_FILENO,
395 "Error: Could not allocate memory for request buffer.\n");
396 goto error_terminate;
397 }
398
399 logprintf(STDOUT_FILENO,
400 "CUSE TPM successfully initialized.\n");
401
402 return 0;
403
404 error_del_pool:
405 g_thread_pool_free(pool, TRUE, TRUE);
406 pool = NULL;
407
408 error_terminate:
409 TPMLIB_Terminate();
410 return -1;
411 }
412
413 /*
414 * convert the blobtype integer into a string that libtpms
415 * understands
416 */
417 static const char *ptm_get_blobname(uint8_t blobtype)
418 {
419 switch (blobtype) {
420 case PTM_BLOB_TYPE_PERMANENT:
421 return TPM_PERMANENT_ALL_NAME;
422 case PTM_BLOB_TYPE_VOLATILE:
423 return TPM_VOLATILESTATE_NAME;
424 case PTM_BLOB_TYPE_SAVESTATE:
425 return TPM_SAVESTATE_NAME;
426 default:
427 return NULL;
428 }
429 }
430
431 static void ptm_open(fuse_req_t req, struct fuse_file_info *fi)
432 {
433 fuse_reply_open(req, fi);
434 }
435
436 /* ptm_write_fatal_error_response
437 *
438 * Write a fatal error response
439 */
440 static void ptm_write_fatal_error_response(void)
441 {
442 if (ptm_res == NULL ||
443 ptm_res_tot < sizeof(TPM_Resp_FatalError)) {
444 ptm_res_tot = sizeof(TPM_Resp_FatalError);
445 TPM_Realloc(&ptm_res, ptm_res_tot);
446 }
447 if (ptm_res) {
448 ptm_res_len = sizeof(TPM_Resp_FatalError);
449 memcpy(ptm_res,
450 TPM_Resp_FatalError,
451 sizeof(TPM_Resp_FatalError));
452 }
453 }
454
455 static void ptm_read(fuse_req_t req, size_t size, off_t off,
456 struct fuse_file_info *fi)
457 {
458 int len;
459
460 if (tpm_running) {
461 /* wait until results are ready */
462 worker_thread_wait_done();
463 }
464
465 len = ptm_res_len;
466
467 if (ptm_res_len > size) {
468 len = size;
469 ptm_res_len -= size;
470 } else {
471 ptm_res_len = 0;
472 }
473
474 fuse_reply_buf(req, (const char *)ptm_res, len);
475 }
476
477 static void ptm_write(fuse_req_t req, const char *buf, size_t size,
478 off_t off, struct fuse_file_info *fi)
479 {
480 ptm_req_len = size;
481 ptm_res_len = 0;
482
483 /* prevent other threads from writing or doing ioctls */
484 g_mutex_lock(FILE_OPS_LOCK);
485
486 if (tpm_running) {
487 /* ensure that we only ever work on one TPM command */
488 if (worker_thread_is_busy()) {
489 fuse_reply_err(req, EBUSY);
490 goto cleanup;
491 }
492
493 /* have command processed by thread pool */
494 if (ptm_req_len > TPM_REQ_MAX)
495 ptm_req_len = TPM_REQ_MAX;
496
497 memcpy(ptm_req, buf, ptm_req_len);
498 msg.type = MESSAGE_TPM_CMD;
499 msg.req = req;
500
501 worker_thread_mark_busy();
502
503 g_thread_pool_push(pool, &msg, NULL);
504
505 fuse_reply_write(req, ptm_req_len);
506 } else {
507 /* TPM not initialized; return error */
508 ptm_write_fatal_error_response();
509 fuse_reply_write(req, ptm_req_len);
510 }
511
512 cleanup:
513 g_mutex_unlock(FILE_OPS_LOCK);
514
515 return;
516 }
517
518 /*
519 * ptm_ioctl : ioctl execution
520 *
521 * req: the fuse_req_t used to send response with
522 * cmd: the ioctl request code
523 * arg: the pointer the application used for calling the ioctl (3rd param)
524 * fi:
525 * flags: some flags provided by fuse
526 * in_buf: the copy of the input buffer
527 * in_bufsz: size of the input buffer; provided by fuse and has size of
528 * needed buffer
529 * out_bufsz: size of the output buffer; provided by fuse and has size of
530 * needed buffer
531 */
532 static void ptm_ioctl(fuse_req_t req, int cmd, void *arg,
533 struct fuse_file_info *fi, unsigned flags,
534 const void *in_buf, size_t in_bufsz, size_t out_bufsz)
535 {
536 TPM_RESULT res;
537 bool exit_prg = FALSE;
538 ptminit_t *init_p;
539 static struct stateblob stateblob;
540
541 if (flags & FUSE_IOCTL_COMPAT) {
542 fuse_reply_err(req, ENOSYS);
543 return;
544 }
545
546 /* some commands have to wait until the worker thread is done */
547 switch(cmd) {
548 case PTM_GET_CAPABILITY:
549 case PTM_SET_LOCALITY:
550 case PTM_CANCEL_TPM_CMD:
551 case PTM_GET_CONFIG:
552 /* no need to wait */
553 break;
554 case PTM_INIT:
555 case PTM_SHUTDOWN:
556 case PTM_GET_TPMESTABLISHED:
557 case PTM_RESET_TPMESTABLISHED:
558 case PTM_HASH_START:
559 case PTM_HASH_DATA:
560 case PTM_HASH_END:
561 case PTM_STORE_VOLATILE:
562 case PTM_GET_STATEBLOB:
563 case PTM_SET_STATEBLOB:
564 if (tpm_running)
565 worker_thread_wait_done();
566 break;
567 }
568
569 /* prevent other threads from writing or doing ioctls */
570 g_mutex_lock(FILE_OPS_LOCK);
571
572 switch (cmd) {
573 case PTM_GET_CAPABILITY:
574 if (!out_bufsz) {
575 struct iovec iov = { arg, sizeof(uint8_t) };
576 fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0);
577 } else {
578 ptmcap_t ptm_caps;
579 ptm_caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN
580 | PTM_CAP_GET_TPMESTABLISHED
581 | PTM_CAP_SET_LOCALITY
582 | PTM_CAP_HASHING
583 | PTM_CAP_CANCEL_TPM_CMD
584 | PTM_CAP_STORE_VOLATILE
585 | PTM_CAP_RESET_TPMESTABLISHED
586 | PTM_CAP_GET_STATEBLOB
587 | PTM_CAP_SET_STATEBLOB
588 | PTM_CAP_STOP
589 | PTM_CAP_GET_CONFIG;
590 fuse_reply_ioctl(req, 0, &ptm_caps, sizeof(ptm_caps));
591 }
592 break;
593
594 case PTM_INIT:
595 init_p = (ptminit_t *)in_buf;
596
597 worker_thread_end();
598
599 TPMLIB_Terminate();
600
601 tpm_running = 0;
602 if ((res = tpm_start(init_p->u.req.init_flags))) {
603 logprintf(STDERR_FILENO,
604 "Error: Could not initialize the TPM.\n");
605 } else {
606 tpm_running = 1;
607 }
608 fuse_reply_ioctl(req, 0, &res, sizeof(res));
609 break;
610
611 case PTM_STOP:
612 worker_thread_end();
613
614 res = TPM_SUCCESS;
615 TPMLIB_Terminate();
616
617 tpm_running = 0;
618
619 TPM_Free(ptm_res);
620 ptm_res = NULL;
621
622 fuse_reply_ioctl(req, 0, &res, sizeof(res));
623
624 break;
625
626 case PTM_SHUTDOWN:
627 worker_thread_end();
628
629 res = TPM_SUCCESS;
630 TPMLIB_Terminate();
631
632 TPM_Free(ptm_res);
633 ptm_res = NULL;
634
635 fuse_reply_ioctl(req, 0, &res, sizeof(res));
636 exit_prg = TRUE;
637
638 break;
639
640 case PTM_GET_TPMESTABLISHED:
641 if (!tpm_running)
642 goto error_not_running;
643
644 if (!out_bufsz) {
645 struct iovec iov = { arg, sizeof(uint8_t) };
646 fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0);
647 } else {
648 ptmest_t te;
649 te.tpm_result = TPM_IO_TpmEstablished_Get(&te.bit);
650 fuse_reply_ioctl(req, 0, &te, sizeof(te));
651 }
652 break;
653
654 case PTM_RESET_TPMESTABLISHED:
655 if (!tpm_running)
656 goto error_not_running;
657
658 if (!in_bufsz) {
659 struct iovec iov = { arg, sizeof(uint32_t) };
660 fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0);
661 } else {
662 ptmreset_est_t *re = (ptmreset_est_t *)in_buf;
663 if (re->u.req.loc > 4) {
664 res = TPM_BAD_LOCALITY;
665 } else {
666 res = _TPM_IO_TpmEstablished_Reset(req, re->u.req.loc);
667 fuse_reply_ioctl(req, 0, &res, sizeof(res));
668 }
669 }
670 break;
671
672 case PTM_SET_LOCALITY:
673 if (!in_bufsz) {
674 struct iovec iov = { arg, sizeof(uint32_t) };
675 fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0);
676 } else {
677 ptmloc_t *l = (ptmloc_t *)in_buf;
678 if (l->u.req.loc > 4) {
679 res = TPM_BAD_LOCALITY;
680 } else {
681 res = 0;
682 locality = l->u.req.loc;
683 }
684 fuse_reply_ioctl(req, 0, &res, sizeof(res));
685 }
686 break;
687
688 case PTM_HASH_START:
689 if (!tpm_running)
690 goto error_not_running;
691
692 res = TPM_IO_Hash_Start();
693 fuse_reply_ioctl(req, 0, &res, sizeof(res));
694 break;
695
696 case PTM_HASH_DATA:
697 if (!tpm_running)
698 goto error_not_running;
699
700 if (!in_bufsz) {
701 struct iovec iov = { arg, sizeof(uint32_t) };
702 fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0);
703 } else {
704 ptmhdata_t *data = (ptmhdata_t *)in_buf;
705 if (data->u.req.length <= sizeof(data->u.req.data)) {
706 res = TPM_IO_Hash_Data(data->u.req.data,
707 data->u.req.length);
708 } else {
709 res = TPM_FAIL;
710 }
711 fuse_reply_ioctl(req, 0, &res, sizeof(res));
712 }
713 break;
714
715 case PTM_HASH_END:
716 if (!tpm_running)
717 goto error_not_running;
718
719 res = TPM_IO_Hash_End();
720 fuse_reply_ioctl(req, 0, &res, sizeof(res));
721 break;
722
723 case PTM_CANCEL_TPM_CMD:
724 if (!tpm_running)
725 goto error_not_running;
726
727 /* for cancellation to work, the TPM would have to
728 * execute in another thread that polls on a cancel
729 * flag
730 */
731 res = TPM_FAIL;
732 fuse_reply_ioctl(req, 0, &res, sizeof(res));
733 break;
734
735 case PTM_STORE_VOLATILE:
736 if (!tpm_running)
737 goto error_not_running;
738
739 res = SWTPM_NVRAM_Store_Volatile();
740 fuse_reply_ioctl(req, 0, &res, sizeof(res));
741 break;
742
743 case PTM_GET_STATEBLOB:
744 if (!tpm_running)
745 goto error_not_running;
746
747 if (!in_bufsz) {
748 struct iovec iov = { arg, sizeof(uint32_t) };
749 fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0);
750 } else {
751 ptm_getstate_t *pgs = (ptm_getstate_t *)in_buf;
752 const char *blobname = ptm_get_blobname(pgs->u.req.type);
753 unsigned char *data = NULL;
754 uint32_t length = 0, to_copy, offset;
755 TPM_BOOL decrypt = ((pgs->u.req.state_flags & STATE_FLAG_DECRYPTED)
756 != 0);
757 TPM_BOOL is_encrypted;
758 uint32_t tpm_number = pgs->u.req.tpm_number;
759 uint32_t blobtype = pgs->u.req.type;
760
761 if (blobname) {
762 offset = pgs->u.req.offset;
763
764 res = SWTPM_NVRAM_GetStateBlob(&data, &length,
765 tpm_number,
766 blobname, decrypt,
767 &is_encrypted);
768 if (data != NULL && length > 0) {
769 to_copy = 0;
770 if (offset < length) {
771 to_copy = min(length - offset,
772 sizeof(pgs->u.resp.data));
773 memcpy(&pgs->u.resp.data, &data[offset], to_copy);
774 }
775
776 pgs->u.resp.length = to_copy;
777 TPM_Free(data);
778 data = NULL;
779
780 pgs->u.resp.state_flags = 0;
781 if (is_encrypted) {
782 pgs->u.resp.state_flags |= STATE_FLAG_ENCRYPTED;
783 }
784 if (blobtype == PTM_BLOB_TYPE_VOLATILE &&
785 to_copy < sizeof(pgs->u.resp.data)) {
786 /* volatile blob deleted once transferred */
787 SWTPM_NVRAM_DeleteName(tpm_number, blobname, FALSE);
788 }
789 } else {
790 /*
791 * blob presumably does not exist; not an error
792 * res would show TPM_RETRY (0x800) flag
793 */
794 pgs->u.resp.length = 0;
795 }
796 } else {
797 res = TPM_BAD_PARAMETER;
798 }
799 pgs->u.resp.tpm_result = res;
800 fuse_reply_ioctl(req, 0, pgs, sizeof(pgs->u.resp));
801 }
802 break;
803
804 case PTM_SET_STATEBLOB:
805 if (tpm_running)
806 goto error_running;
807
808 /* tpm state dir must be set */
809 SWTPM_NVRAM_Init();
810
811 if (!in_bufsz) {
812 struct iovec iov = { arg, sizeof(uint32_t) };
813 fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0);
814 } else {
815 ptm_setstate_t *pss = (ptm_setstate_t *)in_buf;
816 const char *blobname;
817 TPM_BOOL is_encrypted =
818 ((pss->u.req.state_flags & STATE_FLAG_ENCRYPTED) != 0);
819
820 if (pss->u.req.length > sizeof(pss->u.req.data)) {
821 pss->u.resp.tpm_result = TPM_BAD_PARAMETER;
822 fuse_reply_ioctl(req, 0, pss, sizeof(*pss));
823 break;
824 }
825
826 if (stateblob.type != pss->u.req.type) {
827 /* clear old data */
828 TPM_Free(stateblob.data);
829 stateblob.data = NULL;
830 stateblob.length = 0;
831 stateblob.type = pss->u.req.type;
832 }
833
834 /* append */
835 res = TPM_Realloc(&stateblob.data,
836 stateblob.length + pss->u.req.length);
837 if (res != 0) {
838 /* error */
839 TPM_Free(stateblob.data);
840 stateblob.data = NULL;
841 stateblob.length = 0;
842 stateblob.type = 0;
843
844 pss->u.resp.tpm_result = res;
845 fuse_reply_ioctl(req, 0, pss, sizeof(*pss));
846 break;
847 }
848
849 memcpy(&stateblob.data[stateblob.length],
850 pss->u.req.data, pss->u.req.length);
851 stateblob.length += pss->u.req.length;
852
853 if (pss->u.req.length == sizeof(pss->u.req.data)) {
854 /* full packet */
855 pss->u.resp.tpm_result = 0;
856 fuse_reply_ioctl(req, 0, pss, sizeof(*pss));
857 break;
858 }
859 blobname = ptm_get_blobname(pss->u.req.type);
860
861 if (blobname) {
862 res = SWTPM_NVRAM_SetStateBlob(stateblob.data,
863 stateblob.length,
864 is_encrypted,
865 pss->u.req.tpm_number,
866 blobname);
867 } else {
868 res = TPM_BAD_PARAMETER;
869 }
870 TPM_Free(stateblob.data);
871 stateblob.data = NULL;
872 stateblob.length = 0;
873 stateblob.type = 0;
874
875 pss->u.resp.tpm_result = res;
876 fuse_reply_ioctl(req, 0, pss, sizeof(*pss));
877 }
878 break;
879
880 case PTM_GET_CONFIG:
881 if (out_bufsz != sizeof(ptm_getconfig_t)) {
882 struct iovec iov = { arg, sizeof(uint32_t) };
883 fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0);
884 } else {
885 ptm_getconfig_t pgs;
886 pgs.u.resp.tpm_result = 0;
887 pgs.u.resp.flags = 0;
888 if (SWTPM_NVRAM_Has_FileKey())
889 pgs.u.resp.flags |= CONFIG_FLAG_FILE_KEY;
890 if (SWTPM_NVRAM_Has_MigrationKey())
891 pgs.u.resp.flags |= CONFIG_FLAG_MIGRATION_KEY;
892 fuse_reply_ioctl(req, 0, &pgs, sizeof(pgs));
893 }
894 break;
895
896 default:
897 fuse_reply_err(req, EINVAL);
898 }
899
900 cleanup:
901 g_mutex_unlock(FILE_OPS_LOCK);
902
903 if (exit_prg) {
904 logprintf(STDOUT_FILENO,
905 "CUSE TPM is shutting down.\n");
906 exit(0);
907 }
908
909 return;
910
911 error_running:
912 error_not_running:
913 res = TPM_BAD_ORDINAL;
914 fuse_reply_ioctl(req, 0, &res, sizeof(res));
915
916 goto cleanup;
917 }
918
919 static void ptm_init_done(void *userdata) {
920 if (passwd) {
921 if (initgroups(passwd->pw_name, passwd->pw_gid) < 0) {
922 logprintf(STDERR_FILENO,
923 "Error: initgroups(%s, %d) failed.\n",
924 passwd->pw_name, passwd->pw_gid);
925 exit(-10);
926 }
927 if (setgid(passwd->pw_gid) < 0) {
928 logprintf(STDERR_FILENO,
929 "Error: setgid(%d) failed.\n",
930 passwd->pw_gid);
931 exit(-11);
932 }
933 if (setuid(passwd->pw_uid) < 0) {
934 logprintf(STDERR_FILENO,
935 "Error: setuid(%d) failed.\n",
936 passwd->pw_uid);
937 exit(-12);
938 }
939 }
940 }
941
942 static const struct cuse_lowlevel_ops ptm_clop = {
943 .open = ptm_open,
944 .read = ptm_read,
945 .write = ptm_write,
946 .ioctl = ptm_ioctl,
947 .init_done = ptm_init_done,
948 };
949
950 #define PTM_OPT(t, p) { t, offsetof(struct ptm_param, p), 1 }
951
952 static const struct fuse_opt ptm_opts[] = {
953 PTM_OPT("-M %u", major),
954 PTM_OPT("--maj=%u", major),
955 PTM_OPT("-m %u", minor),
956 PTM_OPT("--min=%u", minor),
957 PTM_OPT("-n %s", dev_name),
958 PTM_OPT("--name=%s", dev_name),
959 PTM_OPT("-r %s", runas),
960 PTM_OPT("--runas=%s", runas),
961 PTM_OPT("--log %s", logging),
962 PTM_OPT("--key %s", keydata),
963 PTM_OPT("--migration-key %s", migkeydata),
964 FUSE_OPT_KEY("-h", 0),
965 FUSE_OPT_KEY("--help", 0),
966 FUSE_OPT_KEY("-v", 1),
967 FUSE_OPT_KEY("--version", 1),
968 FUSE_OPT_END
969 };
970
971 static int ptm_process_arg(void *data, const char *arg, int key,
972 struct fuse_args *outargs)
973 {
974 struct ptm_param *param = data;
975
976 switch (key) {
977 case 0:
978 param->is_help = 1;
979 fprintf(stdout, usage, param->prgname);
980 return fuse_opt_add_arg(outargs, "-ho");
981 case 1:
982 param->is_help = 1;
983 fprintf(stdout, "TPM emulator CUSE interface version %d.%d.%d, "
984 "Copyright (c) 2014 IBM Corp.\n",
985 SWTPM_VER_MAJOR,
986 SWTPM_VER_MINOR,
987 SWTPM_VER_MICRO);
988 return 0;
989 default:
990 return -1;
991 }
992 return 0;
993 }
994
995 int main(int argc, char **argv)
996 {
997 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
998 struct ptm_param param = {
999 .major = 0,
1000 .minor = 0,
1001 .dev_name = NULL,
1002 .is_help = 0,
1003 .prgname = argv[0],
1004 .runas = NULL,
1005 .logging = NULL,
1006 .keydata = NULL,
1007 .migkeydata = NULL,
1008 };
1009 char dev_name[128] = "DEVNAME=";
1010 const char *dev_info_argv[] = { dev_name };
1011 struct cuse_info ci;
1012 int ret;
1013
1014 if ((ret = fuse_opt_parse(&args, &param, ptm_opts, ptm_process_arg))) {
1015 fprintf(stderr, "Error: Could not parse option\n");
1016 return ret;
1017 }
1018
1019 if (!param.is_help) {
1020 if (!param.dev_name) {
1021 fprintf(stderr, "Error: device name missing\n");
1022 return -2;
1023 }
1024 strncat(dev_name, param.dev_name, sizeof(dev_name) - 9);
1025 } else {
1026 return 0;
1027 }
1028
1029 if (handle_log_options(param.logging) < 0 ||
1030 handle_key_options(param.keydata) < 0 ||
1031 handle_migration_key_options(param.migkeydata) < 0)
1032 return -3;
1033
1034 if (setuid(0)) {
1035 fprintf(stderr, "Error: Unable to setuid root. uid = %d, "
1036 "euid = %d, gid = %d\n", getuid(), geteuid(), getgid());
1037 return -4;
1038 }
1039
1040 if (param.runas) {
1041 if (!(passwd = getpwnam(param.runas))) {
1042 fprintf(stderr, "User '%s' does not exist\n",
1043 param.runas);
1044 return -5;
1045 }
1046 }
1047
1048 memset(&ci, 0, sizeof(ci));
1049 ci.dev_major = param.major;
1050 ci.dev_minor = param.minor;
1051 ci.dev_info_argc = 1;
1052 ci.dev_info_argv = dev_info_argv;
1053
1054 #if GLIB_MINOR_VERSION >= 32
1055 g_mutex_init(THREAD_BUSY_LOCK);
1056 g_cond_init(THREAD_BUSY_SIGNAL);
1057 g_mutex_init(FILE_OPS_LOCK);
1058 #else
1059 g_thread_init(NULL);
1060 THREAD_BUSY_LOCK = g_mutex_new();
1061 THREAD_BUSY_SIGNAL = g_cond_new();
1062 FILE_OPS_LOCK = g_mutex_new();
1063 #endif
1064
1065 return cuse_lowlevel_main(args.argc, args.argv, &ci, &ptm_clop,
1066 &param);
1067 }