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