2 * ptm - CUSE based TPM PassThrough Multiplexer for QEMU.
4 * This program instantiates one /dev/vtpm* device, and
5 * calls libtpms to handle requests
7 * The following code was derived from
8 * http://fuse.sourceforge.net/doxygen/cusexmp_8c.html
10 * It's original header states:
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.
19 * Authors: David Safford safford@us.ibm.com
20 * Stefan Berger stefanb@us.ibm.com
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.
32 #define FUSE_USE_VERSION 29
42 #include <sys/types.h>
46 #include <arpa/inet.h>
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>
54 #include "cuse_lowlevel.h"
56 #include "tpm_ioctl.h"
58 #include "swtpm_nvfile.h"
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
;
75 #if GLIB_MAJOR_VERSION >= 2
76 # if GLIB_MINOR_VERSION >= 32
78 GCond thread_busy_signal
;
79 GMutex thread_busy_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
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
97 #error Unsupport glib version
119 struct thread_message
{
124 #define min(a,b) ((a) < (b) ? (a) : (b))
132 static const char *usage
=
133 "usage: %s [options]\n"
135 "The following options are supported:\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"
162 "Make sure that TPM_PATH environment variable points to directory\n"
163 "where TPM's NV storage file is kept\n"
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 */
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 */
178 typedef struct TPM_Response_Header
{
182 } __attribute__ ((packed
)) TPM_Response_Header
;
185 ptm_io_getlocality(TPM_MODIFIER_INDICATOR
*loc
, uint32_t tpmnum
)
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
,
200 static struct thread_message msg
;
202 /* worker_thread_wait_done
204 * Wait while the TPM worker thread is busy
206 static void worker_thread_wait_done(void)
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
,
219 * seems like occasionally the g_cond_signal did not wake up
220 * the sleeping task; so we poll [TIS Test in BIOS]
223 abs_time
.tv_usec
= 0;
224 g_cond_timed_wait(THREAD_BUSY_SIGNAL
,
229 g_mutex_unlock(THREAD_BUSY_LOCK
);
232 /* worker_thread_mark_busy
234 * Mark the worker thread as busy; call this with the lock held
236 static void worker_thread_mark_busy(void)
238 g_mutex_lock(THREAD_BUSY_LOCK
);
240 g_mutex_unlock(THREAD_BUSY_LOCK
);
243 /* work_tread_mark_done
245 * Mark the worker thread as done and wake
246 * up the waiting thread
248 static void worker_thread_mark_done(void)
250 g_mutex_lock(THREAD_BUSY_LOCK
);
252 g_cond_signal(THREAD_BUSY_SIGNAL
);
253 g_mutex_unlock(THREAD_BUSY_LOCK
);
256 /* worker_thread_is_busy
258 * Determine whether the worker thread is busy
260 static int worker_thread_is_busy()
265 static void worker_thread(gpointer data
, gpointer user_data
)
267 struct thread_message
*msg
= (struct thread_message
*)data
;
270 case MESSAGE_TPM_CMD
:
271 TPMLIB_Process(&ptm_res
, &ptm_res_len
, &ptm_res_tot
,
272 ptm_req
, ptm_req_len
);
278 /* results are ready */
279 worker_thread_mark_done();
284 * finish the worker thread
286 static void worker_thread_end()
289 worker_thread_wait_done();
290 g_thread_pool_free(pool
, TRUE
, TRUE
);
295 /* _TPM_IO_TpmEstablished_Reset
297 * Reset the TPM Established bit
300 _TPM_IO_TpmEstablished_Reset(fuse_req_t req
,
301 TPM_MODIFIER_INDICATOR locty
)
303 TPM_RESULT res
= TPM_FAIL
;
304 TPM_Response_Header
*tpmrh
;
305 TPM_MODIFIER_INDICATOR orig_locality
= locality
;
309 ptm_req_len
= sizeof(TPM_ResetEstablishmentBit
);
310 memcpy(ptm_req
, TPM_ResetEstablishmentBit
, ptm_req_len
);
311 msg
.type
= MESSAGE_TPM_CMD
;
314 worker_thread_mark_busy();
316 g_thread_pool_push(pool
, &msg
, NULL
);
318 worker_thread_wait_done();
320 if (ptm_res_len
>= sizeof(TPM_Response_Header
)) {
321 tpmrh
= (TPM_Response_Header
*)ptm_res
;
322 res
= ntohl(tpmrh
->returnCode
);
325 locality
= orig_locality
;
330 static int tpm_start(uint32_t flags
)
333 char * tpmdir
= NULL
;
335 /* temporary - the backend script lacks the perms to do this */
336 if (tpmdir
== NULL
) {
337 tpmdir
= getenv("TPM_PATH");
339 logprintf(STDOUT_FILENO
,
340 "Error: TPM_PATH is not set\n");
344 dir
= opendir(tpmdir
);
348 if (mkdir(tpmdir
, 0775)) {
349 logprintf(STDERR_FILENO
,
350 "Error: Could not open TPM_PATH dir\n");
355 pool
= g_thread_pool_new(worker_thread
,
361 logprintf(STDERR_FILENO
,
362 "Error: Could not create the thread pool.\n");
366 if (TPMLIB_RegisterCallbacks(&cbs
) != TPM_SUCCESS
) {
367 logprintf(STDERR_FILENO
,
368 "Error: Could not register the callbacks.\n");
372 if (TPMLIB_MainInit() != TPM_SUCCESS
) {
373 logprintf(STDERR_FILENO
,
374 "Error: Could not start the CUSE TPM.\n");
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
,
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
;
392 ptm_req
= malloc(4096);
394 logprintf(STDERR_FILENO
,
395 "Error: Could not allocate memory for request buffer.\n");
396 goto error_terminate
;
399 logprintf(STDOUT_FILENO
,
400 "CUSE TPM successfully initialized.\n");
405 g_thread_pool_free(pool
, TRUE
, TRUE
);
414 * convert the blobtype integer into a string that libtpms
417 static const char *ptm_get_blobname(uint8_t 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
;
431 static void ptm_open(fuse_req_t req
, struct fuse_file_info
*fi
)
433 fuse_reply_open(req
, fi
);
436 /* ptm_write_fatal_error_response
438 * Write a fatal error response
440 static void ptm_write_fatal_error_response(void)
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
);
448 ptm_res_len
= sizeof(TPM_Resp_FatalError
);
451 sizeof(TPM_Resp_FatalError
));
455 static void ptm_read(fuse_req_t req
, size_t size
, off_t off
,
456 struct fuse_file_info
*fi
)
461 /* wait until results are ready */
462 worker_thread_wait_done();
467 if (ptm_res_len
> size
) {
474 fuse_reply_buf(req
, (const char *)ptm_res
, len
);
477 static void ptm_write(fuse_req_t req
, const char *buf
, size_t size
,
478 off_t off
, struct fuse_file_info
*fi
)
483 /* prevent other threads from writing or doing ioctls */
484 g_mutex_lock(FILE_OPS_LOCK
);
487 /* ensure that we only ever work on one TPM command */
488 if (worker_thread_is_busy()) {
489 fuse_reply_err(req
, EBUSY
);
493 /* have command processed by thread pool */
494 if (ptm_req_len
> TPM_REQ_MAX
)
495 ptm_req_len
= TPM_REQ_MAX
;
497 memcpy(ptm_req
, buf
, ptm_req_len
);
498 msg
.type
= MESSAGE_TPM_CMD
;
501 worker_thread_mark_busy();
503 g_thread_pool_push(pool
, &msg
, NULL
);
505 fuse_reply_write(req
, ptm_req_len
);
507 /* TPM not initialized; return error */
508 ptm_write_fatal_error_response();
509 fuse_reply_write(req
, ptm_req_len
);
513 g_mutex_unlock(FILE_OPS_LOCK
);
519 * ptm_ioctl : ioctl execution
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)
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
529 * out_bufsz: size of the output buffer; provided by fuse and has size of
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
)
537 bool exit_prg
= FALSE
;
539 static struct stateblob stateblob
;
541 if (flags
& FUSE_IOCTL_COMPAT
) {
542 fuse_reply_err(req
, ENOSYS
);
546 /* some commands have to wait until the worker thread is done */
548 case PTM_GET_CAPABILITY
:
549 case PTM_SET_LOCALITY
:
550 case PTM_CANCEL_TPM_CMD
:
552 /* no need to wait */
556 case PTM_GET_TPMESTABLISHED
:
557 case PTM_RESET_TPMESTABLISHED
:
561 case PTM_STORE_VOLATILE
:
562 case PTM_GET_STATEBLOB
:
563 case PTM_SET_STATEBLOB
:
565 worker_thread_wait_done();
569 /* prevent other threads from writing or doing ioctls */
570 g_mutex_lock(FILE_OPS_LOCK
);
573 case PTM_GET_CAPABILITY
:
575 struct iovec iov
= { arg
, sizeof(uint8_t) };
576 fuse_reply_ioctl_retry(req
, &iov
, 1, NULL
, 0);
579 ptm_caps
= PTM_CAP_INIT
| PTM_CAP_SHUTDOWN
580 | PTM_CAP_GET_TPMESTABLISHED
581 | PTM_CAP_SET_LOCALITY
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
589 | PTM_CAP_GET_CONFIG
;
590 fuse_reply_ioctl(req
, 0, &ptm_caps
, sizeof(ptm_caps
));
595 init_p
= (ptminit_t
*)in_buf
;
602 if ((res
= tpm_start(init_p
->u
.req
.init_flags
))) {
603 logprintf(STDERR_FILENO
,
604 "Error: Could not initialize the TPM.\n");
608 fuse_reply_ioctl(req
, 0, &res
, sizeof(res
));
622 fuse_reply_ioctl(req
, 0, &res
, sizeof(res
));
635 fuse_reply_ioctl(req
, 0, &res
, sizeof(res
));
640 case PTM_GET_TPMESTABLISHED
:
642 goto error_not_running
;
645 struct iovec iov
= { arg
, sizeof(uint8_t) };
646 fuse_reply_ioctl_retry(req
, &iov
, 1, NULL
, 0);
649 te
.tpm_result
= TPM_IO_TpmEstablished_Get(&te
.bit
);
650 fuse_reply_ioctl(req
, 0, &te
, sizeof(te
));
654 case PTM_RESET_TPMESTABLISHED
:
656 goto error_not_running
;
659 struct iovec iov
= { arg
, sizeof(uint32_t) };
660 fuse_reply_ioctl_retry(req
, &iov
, 1, NULL
, 0);
662 ptmreset_est_t
*re
= (ptmreset_est_t
*)in_buf
;
663 if (re
->u
.req
.loc
> 4) {
664 res
= TPM_BAD_LOCALITY
;
666 res
= _TPM_IO_TpmEstablished_Reset(req
, re
->u
.req
.loc
);
667 fuse_reply_ioctl(req
, 0, &res
, sizeof(res
));
672 case PTM_SET_LOCALITY
:
674 struct iovec iov
= { arg
, sizeof(uint32_t) };
675 fuse_reply_ioctl_retry(req
, &iov
, 1, NULL
, 0);
677 ptmloc_t
*l
= (ptmloc_t
*)in_buf
;
678 if (l
->u
.req
.loc
> 4) {
679 res
= TPM_BAD_LOCALITY
;
682 locality
= l
->u
.req
.loc
;
684 fuse_reply_ioctl(req
, 0, &res
, sizeof(res
));
690 goto error_not_running
;
692 res
= TPM_IO_Hash_Start();
693 fuse_reply_ioctl(req
, 0, &res
, sizeof(res
));
698 goto error_not_running
;
701 struct iovec iov
= { arg
, sizeof(uint32_t) };
702 fuse_reply_ioctl_retry(req
, &iov
, 1, NULL
, 0);
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
,
711 fuse_reply_ioctl(req
, 0, &res
, sizeof(res
));
717 goto error_not_running
;
719 res
= TPM_IO_Hash_End();
720 fuse_reply_ioctl(req
, 0, &res
, sizeof(res
));
723 case PTM_CANCEL_TPM_CMD
:
725 goto error_not_running
;
727 /* for cancellation to work, the TPM would have to
728 * execute in another thread that polls on a cancel
732 fuse_reply_ioctl(req
, 0, &res
, sizeof(res
));
735 case PTM_STORE_VOLATILE
:
737 goto error_not_running
;
739 res
= SWTPM_NVRAM_Store_Volatile();
740 fuse_reply_ioctl(req
, 0, &res
, sizeof(res
));
743 case PTM_GET_STATEBLOB
:
745 goto error_not_running
;
748 struct iovec iov
= { arg
, sizeof(uint32_t) };
749 fuse_reply_ioctl_retry(req
, &iov
, 1, NULL
, 0);
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
)
757 TPM_BOOL is_encrypted
;
758 uint32_t tpm_number
= pgs
->u
.req
.tpm_number
;
759 uint32_t blobtype
= pgs
->u
.req
.type
;
762 offset
= pgs
->u
.req
.offset
;
764 res
= SWTPM_NVRAM_GetStateBlob(&data
, &length
,
768 if (data
!= NULL
&& length
> 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
);
776 pgs
->u
.resp
.length
= to_copy
;
780 pgs
->u
.resp
.state_flags
= 0;
782 pgs
->u
.resp
.state_flags
|= STATE_FLAG_ENCRYPTED
;
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
);
791 * blob presumably does not exist; not an error
792 * res would show TPM_RETRY (0x800) flag
794 pgs
->u
.resp
.length
= 0;
797 res
= TPM_BAD_PARAMETER
;
799 pgs
->u
.resp
.tpm_result
= res
;
800 fuse_reply_ioctl(req
, 0, pgs
, sizeof(pgs
->u
.resp
));
804 case PTM_SET_STATEBLOB
:
808 /* tpm state dir must be set */
812 struct iovec iov
= { arg
, sizeof(uint32_t) };
813 fuse_reply_ioctl_retry(req
, &iov
, 1, NULL
, 0);
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);
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
));
826 if (stateblob
.type
!= pss
->u
.req
.type
) {
828 TPM_Free(stateblob
.data
);
829 stateblob
.data
= NULL
;
830 stateblob
.length
= 0;
831 stateblob
.type
= pss
->u
.req
.type
;
835 res
= TPM_Realloc(&stateblob
.data
,
836 stateblob
.length
+ pss
->u
.req
.length
);
839 TPM_Free(stateblob
.data
);
840 stateblob
.data
= NULL
;
841 stateblob
.length
= 0;
844 pss
->u
.resp
.tpm_result
= res
;
845 fuse_reply_ioctl(req
, 0, pss
, sizeof(*pss
));
849 memcpy(&stateblob
.data
[stateblob
.length
],
850 pss
->u
.req
.data
, pss
->u
.req
.length
);
851 stateblob
.length
+= pss
->u
.req
.length
;
853 if (pss
->u
.req
.length
== sizeof(pss
->u
.req
.data
)) {
855 pss
->u
.resp
.tpm_result
= 0;
856 fuse_reply_ioctl(req
, 0, pss
, sizeof(*pss
));
859 blobname
= ptm_get_blobname(pss
->u
.req
.type
);
862 res
= SWTPM_NVRAM_SetStateBlob(stateblob
.data
,
865 pss
->u
.req
.tpm_number
,
868 res
= TPM_BAD_PARAMETER
;
870 TPM_Free(stateblob
.data
);
871 stateblob
.data
= NULL
;
872 stateblob
.length
= 0;
875 pss
->u
.resp
.tpm_result
= res
;
876 fuse_reply_ioctl(req
, 0, pss
, sizeof(*pss
));
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);
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
));
897 fuse_reply_err(req
, EINVAL
);
901 g_mutex_unlock(FILE_OPS_LOCK
);
904 logprintf(STDOUT_FILENO
,
905 "CUSE TPM is shutting down.\n");
913 res
= TPM_BAD_ORDINAL
;
914 fuse_reply_ioctl(req
, 0, &res
, sizeof(res
));
919 static void ptm_init_done(void *userdata
) {
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
);
927 if (setgid(passwd
->pw_gid
) < 0) {
928 logprintf(STDERR_FILENO
,
929 "Error: setgid(%d) failed.\n",
933 if (setuid(passwd
->pw_uid
) < 0) {
934 logprintf(STDERR_FILENO
,
935 "Error: setuid(%d) failed.\n",
942 static const struct cuse_lowlevel_ops ptm_clop
= {
947 .init_done
= ptm_init_done
,
950 #define PTM_OPT(t, p) { t, offsetof(struct ptm_param, p), 1 }
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),
971 static int ptm_process_arg(void *data
, const char *arg
, int key
,
972 struct fuse_args
*outargs
)
974 struct ptm_param
*param
= data
;
979 fprintf(stdout
, usage
, param
->prgname
);
980 return fuse_opt_add_arg(outargs
, "-ho");
983 fprintf(stdout
, "TPM emulator CUSE interface version %d.%d.%d, "
984 "Copyright (c) 2014 IBM Corp.\n",
995 int main(int argc
, char **argv
)
997 struct fuse_args args
= FUSE_ARGS_INIT(argc
, argv
);
998 struct ptm_param param
= {
1009 char dev_name
[128] = "DEVNAME=";
1010 const char *dev_info_argv
[] = { dev_name
};
1011 struct cuse_info ci
;
1014 if ((ret
= fuse_opt_parse(&args
, ¶m
, ptm_opts
, ptm_process_arg
))) {
1015 fprintf(stderr
, "Error: Could not parse option\n");
1019 if (!param
.is_help
) {
1020 if (!param
.dev_name
) {
1021 fprintf(stderr
, "Error: device name missing\n");
1024 strncat(dev_name
, param
.dev_name
, sizeof(dev_name
) - 9);
1029 if (handle_log_options(param
.logging
) < 0 ||
1030 handle_key_options(param
.keydata
) < 0 ||
1031 handle_migration_key_options(param
.migkeydata
) < 0)
1035 fprintf(stderr
, "Error: Unable to setuid root. uid = %d, "
1036 "euid = %d, gid = %d\n", getuid(), geteuid(), getgid());
1041 if (!(passwd
= getpwnam(param
.runas
))) {
1042 fprintf(stderr
, "User '%s' does not exist\n",
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
;
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
);
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();
1065 return cuse_lowlevel_main(args
.argc
, args
.argv
, &ci
, &ptm_clop
,