#include "common.h"
#include "tpmstate.h"
#include "pidfile.h"
+#include "locality.h"
#include "logging.h"
#include "tpm_ioctl.h"
-#include "swtpm_nvfile.h"
+#include "swtpm_nvstore.h"
#include "tpmlib.h"
#include "main.h"
#include "utils.h"
#include "threadpool.h"
+#include "seccomp_profile.h"
+#include "options.h"
+#include "capabilities.h"
+#include "swtpm_utils.h"
/* maximum size of request buffer */
#define TPM_REQ_MAX 4096
+/* version of the TPM (1.2 or 2) */
+static TPMLIB_TPMVersion tpmversion;
+
/* buffer containing the TPM request */
static unsigned char *ptm_request;
/* buffer containing the TPM response */
static unsigned char *ptm_response;
+/* offset from where to read from; reset when ptm_response is set */
+static size_t ptm_read_offset;
+
/* the sizes of the data in the buffers */
static uint32_t ptm_req_len, ptm_res_len, ptm_res_tot;
/* whether the TPM is running (TPM_Init was received) */
static bool tpm_running;
+/* flags on how to handle locality */
+static uint32_t locality_flags;
+
+/* the fuse_session that we will signal an exit to to exit the prg. */
+static struct fuse_session *ptm_fuse_session;
+
#if GLIB_MAJOR_VERSION >= 2
# if GLIB_MINOR_VERSION >= 32
-GMutex file_ops_lock;
+static GMutex file_ops_lock;
# define FILE_OPS_LOCK &file_ops_lock
# else
-GMutex *file_ops_lock;
+static GMutex *file_ops_lock;
# define FILE_OPS_LOCK file_ops_lock
# endif
char *migkeydata;
char *piddata;
char *tpmstatedata;
+ char *localitydata;
+ char *seccompdata;
+ unsigned int seccomp_action;
+ char *flagsdata;
+ uint16_t startupType;
};
/* single message to send to the worker thread */
static struct thread_message msg;
-#define min(a,b) ((a) < (b) ? (a) : (b))
-
struct stateblob {
uint8_t type;
uint8_t *data;
} stateblob_desc;
typedef enum tx_state_type {
+ TX_STATE_CLOSED = 0,
TX_STATE_RW_COMMAND = 1,
TX_STATE_SET_STATE_BLOB = 2,
TX_STATE_GET_STATE_BLOB = 3,
"-n NAME|--name=NAME : device name (mandatory)\n"
"-M MAJ|--maj=MAJ : device major number\n"
"-m MIN|--min=MIN : device minor number\n"
-"--key file=<path>[,mode=aes-cbc][,format=hex|binary][,remove=[true|false]]\n"
+"--key file=<path>|fd=<fd>[,mode=aes-cbc|aes-256-cbc][,format=hex|binary][,remove=[true|false]]\n"
" : use an AES key for the encryption of the TPM's state\n"
" files; use the given mode for the block encryption;\n"
" the key is to be provided as a hex string or in binary\n"
" format; the keyfile can be automatically removed using\n"
" the remove parameter\n"
-"--key pwdfile=<path>[,mode=aes-cbc][,remove=[true|false]]\n"
+"--key pwdfile=<path>|pwdfd=<fd>[,mode=aes-cbc|aes-256-cbc][,remove=[true|false]][,kdf=sha512|pbkdf2]\n"
" : provide a passphrase in a file; the AES key will be\n"
-" derived from this passphrase\n"
-"--migration-key file=<path>[,mode=aes-cbc][,format=hex|binary][,remove=[true|false]]\n"
+" derived from this passphrase; default kdf is PBKDF2\n"
+"--locality [reject-locality-4][,allow-set-locality]\n"
+" : reject-locality-4: reject any command in locality 4\n"
+" allow-set-locality: accept SetLocality command\n"
+"--migration-key file=<path>|fd=<fd>[,mode=aes-cbc|aes-256-cbc][,format=hex|binary][,remove=[true|false]]\n"
" : use an AES key for the encryption of the TPM's state\n"
" when it is retrieved from the TPM via ioctls;\n"
" Setting this key ensures that the TPM's state will always\n"
" be encrypted when migrated\n"
-"--migration-key pwdfile=<path>[,mode=aes-cbc][,remove=[true|false]]\n"
+"--migration-key pwdfile=<path>|pwdfd=<fd>[,mode=aes-cbc|aes-256-cbc][,remove=[true|false]][,kdf=sha512|pbkdf2]\n"
" : provide a passphrase in a file; the AES key will be\n"
-" derived from this passphrase\n"
-"--log file=<path>|fd=<filedescriptor>[,level=n]\n"
+" derived from this passphrase; default kdf is PBKDF2\n"
+"--log file=<path>|fd=<filedescriptor>[,level=n][,prefix=<prefix>][,truncate]\n"
" : write the TPM's log into the given file rather than\n"
" to the console; provide '-' for path to avoid logging\n"
-" log level 5 and higher will enable libtpms logging\n"
-"--pid file=<path> : write the process ID into the given file\n"
-"--tpmstate dir=<dir>\n"
-" : set the directory where the TPM's state will be written\n"
+" log level 5 and higher will enable libtpms logging;\n"
+" all logged output will be prefixed with prefix;\n"
+" the log file can be reset (truncate)\n"
+"--pid file=<path>|fd=<filedescriptor>\n"
+" : write the process ID into the given file\n"
+"--tpmstate dir=<dir>[,mode=0...]|backend-uri=<uri>\n"
+" : set the directory or uri where the TPM's state will be written\n"
" into; the TPM_PATH environment variable can be used\n"
-" instead\n"
+" instead of dir option;\n"
+" mode allows a user to set the file mode bits of the state\n"
+" files; the default mode is 0640;\n"
+"--flags [not-need-init][,startup-clear|startup-state|startup-deactivated|startup-none]\n"
+" : not-need-init: commands can be sent without needing to\n"
+" send an INIT via control channel;\n"
+" startup-...: send Startup command with this type;\n"
"-r|--runas <user> : after creating the CUSE device, change to the given\n"
" user\n"
-""
+"--tpm2 : choose TPM2 functionality\n"
+#ifdef WITH_SECCOMP
+# ifndef SCMP_ACT_LOG
+"--seccomp action=none|kill\n"
+# else
+"--seccomp action=none|kill|log\n"
+# endif
+" : Choose the action of the seccomp profile when a\n"
+" blacklisted syscall is executed; default is kill\n"
+#endif
+"--print-capabilites : print capabilities and terminate\n"
+"--print-states : print existing TPM states and terminate\n"
"-h|--help : display this help screen and terminate\n"
"\n";
-const static unsigned char TPM_Resp_FatalError[] = {
- 0x00, 0xC4, /* TPM Response */
- 0x00, 0x00, 0x00, 0x0A, /* length (10) */
- 0x00, 0x00, 0x00, 0x09 /* TPM_FAIL */
-};
-
-const static unsigned char TPM_ResetEstablishmentBit[] = {
- 0x00, 0xC1, /* TPM Request */
- 0x00, 0x00, 0x00, 0x0A, /* length (10) */
- 0x40, 0x00, 0x00, 0x0B /* TPM_ORD_ResetEstablishmentBit */
-};
-
static TPM_RESULT
-ptm_io_getlocality(TPM_MODIFIER_INDICATOR *loc, uint32_t tpmnum)
+ptm_io_getlocality(TPM_MODIFIER_INDICATOR *loc,
+ uint32_t tpmnum SWTPM_ATTR_UNUSED)
{
*loc = locality;
return TPM_SUCCESS;
/* the current state the transfer interface is in */
static transfer_state tx_state;
+/* function prototypes */
+static void ptm_cleanup(void);
+
/************************* cached stateblob *********************************/
static stateblob_desc cached_stateblob;
*/
static void cached_stateblob_free(void)
{
- TPM_Free(cached_stateblob.data);
+ free(cached_stateblob.data);
cached_stateblob.data = NULL;
cached_stateblob.data_length = 0;
}
/*
* worker_thread: the worker thread
*/
-static void worker_thread(gpointer data, gpointer user_data)
+static void worker_thread(gpointer data, gpointer user_data SWTPM_ATTR_UNUSED)
{
struct thread_message *msg = (struct thread_message *)data;
case MESSAGE_TPM_CMD:
TPMLIB_Process(&ptm_response, &ptm_res_len, &ptm_res_tot,
ptm_request, ptm_req_len);
+ ptm_read_offset = 0;
break;
case MESSAGE_IOCTL:
break;
/***************************** utility functions ****************************/
-/* _TPM_IO_TpmEstablished_Reset
- *
- * Reset the TPM Established bit by creating a TPM_ResetEstablishmentBit
- * command and sending it to the TPM; we temporarily switch the locality
- * to the one provded to this call. We wait until the TPM has processed
- * the request.
- */
-static TPM_RESULT _TPM_IO_TpmEstablished_Reset(fuse_req_t req,
- TPM_MODIFIER_INDICATOR locty)
-{
- TPM_RESULT res = TPM_FAIL;
- TPM_Response_Header *tpmrh;
- TPM_MODIFIER_INDICATOR orig_locality = locality;
-
- locality = locty;
-
- ptm_req_len = sizeof(TPM_ResetEstablishmentBit);
- memcpy(ptm_request, TPM_ResetEstablishmentBit, ptm_req_len);
-
- msg.type = MESSAGE_TPM_CMD;
-
- worker_thread_mark_busy();
-
- g_thread_pool_push(pool, &msg, NULL);
-
- worker_thread_wait_done();
-
- if (ptm_res_len >= sizeof(TPM_Response_Header)) {
- tpmrh = (TPM_Response_Header *)ptm_response;
- res = ntohl(tpmrh->returnCode);
- }
-
- locality = orig_locality;
-
- return res;
-}
-
/*
* tpm_start: Start the TPM
*
* libtpms and allocate a global TPM request buffer.
*
* @flags: libtpms init flags
+ * @l_tpmversion: the version of the TPM
+ * @res: the result from starting the TPM
*/
-static int tpm_start(uint32_t flags)
+static int tpm_start(uint32_t flags, TPMLIB_TPMVersion l_tpmversion,
+ TPM_RESULT *res)
{
DIR *dir;
- const char *tpmdir = tpmstate_get_dir();
+ const char *uri = tpmstate_get_backend_uri();
+ const char *tpmdir = uri + strlen("dir://");
+
+ *res = TPM_FAIL;
dir = opendir(tpmdir);
if (dir) {
pool = g_thread_pool_new(worker_thread,
NULL,
1,
- TRUE,
+ FALSE,
NULL);
if (!pool) {
logprintf(STDERR_FILENO,
goto error_del_pool;
}
- if (tpmlib_start(&cbs, flags) != TPM_SUCCESS)
+ *res = tpmlib_start(flags, l_tpmversion);
+ if (*res != TPM_SUCCESS)
goto error_del_pool;
logprintf(STDOUT_FILENO,
*
* Write a fatal error response into the global ptm_response buffer.
*/
-static void ptm_write_fatal_error_response(void)
+static void ptm_write_fatal_error_response(TPMLIB_TPMVersion l_tpmversion)
+{
+ tpmlib_write_fatal_error_response(&ptm_response,
+ &ptm_res_len,
+ &ptm_res_tot,
+ l_tpmversion);
+ ptm_read_offset = 0;
+}
+
+/*
+ * ptm_send_startup: Send a TPM/TPM2_Startup
+ */
+static int ptm_send_startup(uint16_t startupType,
+ TPMLIB_TPMVersion l_tpmversion SWTPM_ATTR_UNUSED)
{
- if (ptm_response == NULL ||
- ptm_res_tot < sizeof(TPM_Resp_FatalError)) {
- ptm_res_tot = sizeof(TPM_Resp_FatalError);
- TPM_Realloc(&ptm_response, ptm_res_tot);
+ uint32_t command_length;
+ unsigned char command[sizeof(struct tpm_startup)];
+ uint32_t max_command_length = sizeof(command);
+ int ret = 0;
+ TPM_RESULT rc = TPM_SUCCESS;
+
+ command_length = tpmlib_create_startup_cmd(
+ startupType,
+ tpmversion,
+ command, max_command_length);
+ if (command_length > 0) {
+ rc = TPMLIB_Process(&ptm_response, &ptm_res_len, &ptm_res_tot,
+ (unsigned char *)command, command_length);
+ ptm_read_offset = 0;
}
- if (ptm_response) {
- ptm_res_len = sizeof(TPM_Resp_FatalError);
- memcpy(ptm_response,
- TPM_Resp_FatalError,
- sizeof(TPM_Resp_FatalError));
+
+ if (rc || command_length == 0) {
+ if (rc) {
+ logprintf(STDERR_FILENO, "Could not send Startup: 0x%x\n", rc);
+ ret = -1;
+ }
}
+
+ return ret;
}
/************************************ read() support ***************************/
*/
static void ptm_read_result(fuse_req_t req, size_t size)
{
- int len;
+ size_t len = 0;
+
+ /* prevent other threads from reading or writing cmds or doing ioctls */
+ g_mutex_lock(FILE_OPS_LOCK);
if (tpm_running) {
/* wait until results are ready */
worker_thread_wait_done();
}
- len = ptm_res_len;
-
- if (ptm_res_len > size) {
- len = size;
- ptm_res_len -= size;
- } else {
- ptm_res_len = 0;
+ if (ptm_read_offset < ptm_res_len) {
+ len = ptm_res_len - ptm_read_offset;
+ if (size < len)
+ len = size;
}
- fuse_reply_buf(req, (const char *)ptm_response, len);
+ fuse_reply_buf(req, (const char *)&ptm_response[ptm_read_offset], len);
+
+ ptm_read_offset += len;
+
+ g_mutex_unlock(FILE_OPS_LOCK);
}
/*
* Depending on the current state of the transfer interface (read/write)
* return either the results of TPM commands or a data of a TPM state blob.
*/
-static void ptm_read(fuse_req_t req, size_t size, off_t off,
- struct fuse_file_info *fi)
+static void ptm_read(fuse_req_t req, size_t size, off_t off SWTPM_ATTR_UNUSED,
+ struct fuse_file_info *fi SWTPM_ATTR_UNUSED)
{
switch (tx_state.state) {
case TX_STATE_RW_COMMAND:
case TX_STATE_GET_STATE_BLOB:
ptm_read_stateblob(req, size);
break;
+ case TX_STATE_CLOSED:
+ /* not possible */
+ break;
}
}
const unsigned char *data, uint32_t length,
bool is_encrypted, bool is_last)
{
- const char *blobname;
TPM_RESULT res = 0;
static struct stateblob stateblob;
+ unsigned char *tmp;
if (stateblob.type != blobtype) {
/* new blob; clear old data */
- TPM_Free(stateblob.data);
+ free(stateblob.data);
stateblob.data = NULL;
stateblob.length = 0;
stateblob.type = blobtype;
}
/* append */
- res = TPM_Realloc(&stateblob.data, stateblob.length + length);
- if (res != 0) {
+ tmp = realloc(stateblob.data, stateblob.length + length);
+ if (!tmp) {
+ logprintf(STDERR_FILENO,
+ "Could not allocate %u bytes.\n", stateblob.length + length);
/* error */
- TPM_Free(stateblob.data);
+ free(stateblob.data);
stateblob.data = NULL;
stateblob.length = 0;
stateblob.type = 0;
- return res;
- }
+ return TPM_FAIL;
+ } else
+ stateblob.data = tmp;
memcpy(&stateblob.data[stateblob.length], data, length);
stateblob.length += length;
/* full packet -- expecting more data */
return res;
}
- blobname = tpmlib_get_blobname(blobtype);
-
- if (blobname) {
- res = SWTPM_NVRAM_SetStateBlob(stateblob.data,
- stateblob.length,
- stateblob.is_encrypted,
- 0 /* tpm_number */,
- blobname);
- } else {
- res = TPM_BAD_PARAMETER;
- }
- TPM_Free(stateblob.data);
+ res = SWTPM_NVRAM_SetStateBlob(stateblob.data,
+ stateblob.length,
+ stateblob.is_encrypted,
+ 0 /* tpm_number */,
+ blobtype);
+
+ logprintf(STDERR_FILENO,
+ "Deserialized state type %d (%s), length=%d, res=%d\n",
+ blobtype, tpmlib_get_blobname(blobtype),
+ stateblob.length, res);
+
+ free(stateblob.data);
stateblob.data = NULL;
stateblob.length = 0;
stateblob.type = 0;
pgs->u.resp.length = copied;
pgs->u.resp.totlength = totlength;
pgs->u.resp.tpm_result = res;
+ logprintf(STDERR_FILENO,
+ "Serialized state type %d, length=%d, totlength=%d, res=%d\n",
+ blobtype, copied, totlength, res);
if (res == 0) {
if (offset + copied < totlength) {
* req: fuse_req_t
* buf: the buffer containing the TPM command
* size: the size of the buffer
+ * tpmversion: the version of the TPM
*/
-static void ptm_write_cmd(fuse_req_t req, const char *buf, size_t size)
+static void ptm_write_cmd(fuse_req_t req, const char *buf, size_t size,
+ TPMLIB_TPMVersion l_tpmversion)
{
ptm_req_len = size;
ptm_res_len = 0;
- /* prevent other threads from writing or doing ioctls */
+ /* prevent other threads from reading or writing cmds or doing ioctls */
g_mutex_lock(FILE_OPS_LOCK);
if (tpm_running) {
if (ptm_req_len > TPM_REQ_MAX)
ptm_req_len = TPM_REQ_MAX;
- if (tpmlib_is_request_cancelable(ptm_request, ptm_req_len)) {
+ /* process SetLocality command, if */
+ tpmlib_process(&ptm_response, &ptm_res_len, &ptm_res_tot,
+ (unsigned char *)buf, ptm_req_len,
+ locality_flags, &locality, tpmversion);
+ if (ptm_res_len) {
+ ptm_read_offset = 0;
+ goto skip_process;
+ }
+
+ if (tpmlib_is_request_cancelable(l_tpmversion,
+ (const unsigned char*)buf,
+ ptm_req_len)) {
/* have command processed by thread pool */
memcpy(ptm_request, buf, ptm_req_len);
/* direct processing */
TPMLIB_Process(&ptm_response, &ptm_res_len, &ptm_res_tot,
(unsigned char *)buf, ptm_req_len);
+ ptm_read_offset = 0;
}
} else {
/* TPM not initialized; return error */
- ptm_write_fatal_error_response();
+ ptm_write_fatal_error_response(l_tpmversion);
}
+skip_process:
fuse_reply_write(req, ptm_req_len);
cleanup:
* on what is being transferred using the write()
*/
static void ptm_write(fuse_req_t req, const char *buf, size_t size,
- off_t off, struct fuse_file_info *fi)
+ off_t off SWTPM_ATTR_UNUSED,
+ struct fuse_file_info *fi SWTPM_ATTR_UNUSED)
{
switch (tx_state.state) {
case TX_STATE_RW_COMMAND:
- ptm_write_cmd(req, buf, size);
+ ptm_write_cmd(req, buf, size, tpmversion);
break;
case TX_STATE_GET_STATE_BLOB:
fuse_reply_err(req, EIO);
case TX_STATE_SET_STATE_BLOB:
ptm_write_stateblob(req, buf, size);
break;
+ case TX_STATE_CLOSED:
+ /* not possible */
+ break;
}
}
*/
static void ptm_open(fuse_req_t req, struct fuse_file_info *fi)
{
+ if (tx_state.state != TX_STATE_CLOSED) {
+ fuse_reply_err(req, EBUSY);
+ return;
+ }
+
tx_state.state = TX_STATE_RW_COMMAND;
fuse_reply_open(req, fi);
}
+/*
+ * ptm_release:
+ */
+static void ptm_release(fuse_req_t req, struct fuse_file_info *fi)
+{
+ tx_state.state = TX_STATE_CLOSED;
+
+ fuse_reply_err(req, 0);
+}
+
/*
* ptm_ioctl : ioctl execution
*
* needed buffer
*/
static void ptm_ioctl(fuse_req_t req, int cmd, void *arg,
- struct fuse_file_info *fi, unsigned flags,
+ struct fuse_file_info *fi SWTPM_ATTR_UNUSED,
+ unsigned flags SWTPM_ATTR_UNUSED,
const void *in_buf, size_t in_bufsz, size_t out_bufsz)
{
TPM_RESULT res = TPM_FAIL;
bool exit_prg = FALSE;
ptm_init *init_p;
-
- if (flags & FUSE_IOCTL_COMPAT) {
- fuse_reply_err(req, ENOSYS);
- return;
- }
+ TPM_MODIFIER_INDICATOR orig_locality;
/* some commands have to wait until the worker thread is done */
switch(cmd) {
case PTM_SET_LOCALITY:
case PTM_CANCEL_TPM_CMD:
case PTM_GET_CONFIG:
+ case PTM_SET_BUFFERSIZE:
/* no need to wait */
break;
case PTM_INIT:
fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0);
} else {
ptm_cap ptm_caps;
- ptm_caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN
- | PTM_CAP_GET_TPMESTABLISHED
- | PTM_CAP_SET_LOCALITY
- | PTM_CAP_HASHING
- | PTM_CAP_CANCEL_TPM_CMD
- | PTM_CAP_STORE_VOLATILE
- | PTM_CAP_RESET_TPMESTABLISHED
- | PTM_CAP_GET_STATEBLOB
- | PTM_CAP_SET_STATEBLOB
- | PTM_CAP_STOP
- | PTM_CAP_GET_CONFIG;
+ switch (tpmversion) {
+ case TPMLIB_TPM_VERSION_2:
+ ptm_caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN
+ | PTM_CAP_GET_TPMESTABLISHED
+ | PTM_CAP_SET_LOCALITY
+ | PTM_CAP_HASHING
+ | PTM_CAP_CANCEL_TPM_CMD
+ | PTM_CAP_STORE_VOLATILE
+ | PTM_CAP_RESET_TPMESTABLISHED
+ | PTM_CAP_GET_STATEBLOB
+ | PTM_CAP_SET_STATEBLOB
+ | PTM_CAP_STOP
+ | PTM_CAP_GET_CONFIG
+ | PTM_CAP_SET_BUFFERSIZE
+ | PTM_CAP_GET_INFO;
+ break;
+ case TPMLIB_TPM_VERSION_1_2:
+ ptm_caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN
+ | PTM_CAP_GET_TPMESTABLISHED
+ | PTM_CAP_SET_LOCALITY
+ | PTM_CAP_HASHING
+ | PTM_CAP_CANCEL_TPM_CMD
+ | PTM_CAP_STORE_VOLATILE
+ | PTM_CAP_RESET_TPMESTABLISHED
+ | PTM_CAP_GET_STATEBLOB
+ | PTM_CAP_SET_STATEBLOB
+ | PTM_CAP_STOP
+ | PTM_CAP_GET_CONFIG
+ | PTM_CAP_SET_BUFFERSIZE
+ | PTM_CAP_GET_INFO;
+ break;
+ }
fuse_reply_ioctl(req, 0, &ptm_caps, sizeof(ptm_caps));
}
break;
TPMLIB_Terminate();
tpm_running = false;
- if (tpm_start(init_p->u.req.init_flags) < 0) {
- res = TPM_FAIL;
+ if (tpm_start(init_p->u.req.init_flags, tpmversion, &res) < 0) {
logprintf(STDERR_FILENO,
"Error: Could not initialize the TPM.\n");
} else {
- res = TPM_SUCCESS;
tpm_running = true;
}
init_p->u.resp.tpm_result = res;
tpm_running = false;
- TPM_Free(ptm_response);
+ free(ptm_response);
ptm_response = NULL;
fuse_reply_ioctl(req, 0, &res, sizeof(res));
res = TPM_SUCCESS;
TPMLIB_Terminate();
- TPM_Free(ptm_response);
+ free(ptm_response);
ptm_response = NULL;
fuse_reply_ioctl(req, 0, &res, sizeof(res));
fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0);
} else {
ptm_est te;
+ memset(&te, 0, sizeof(te));
te.u.resp.tpm_result = TPM_IO_TpmEstablished_Get(&te.u.resp.bit);
fuse_reply_ioctl(req, 0, &te, sizeof(te));
}
if (re->u.req.loc > 4) {
res = TPM_BAD_LOCALITY;
} else {
- res = _TPM_IO_TpmEstablished_Reset(req, re->u.req.loc);
+ /* set locality and reset flag in one command */
+ orig_locality = locality;
+ locality = re->u.req.loc;
+
+ res = TPM_IO_TpmEstablished_Reset();
+
+ locality = orig_locality;
fuse_reply_ioctl(req, 0, &res, sizeof(res));
}
}
fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0);
} else {
ptm_loc *l = (ptm_loc *)in_buf;
- if (l->u.req.loc > 4) {
+ if (l->u.req.loc > 4 ||
+ (l->u.req.loc == 4 &&
+ locality_flags & LOCALITY_FLAG_REJECT_LOCALITY_4)) {
res = TPM_BAD_LOCALITY;
} else {
res = TPM_SUCCESS;
* execute in another thread that polls on a cancel
* flag
*/
- res = TPM_FAIL;
+ res = TPMLIB_CancelCommand();
fuse_reply_ioctl(req, 0, &res, sizeof(res));
break;
}
break;
+ case PTM_SET_BUFFERSIZE:
+ if (out_bufsz != sizeof(ptm_setbuffersize)) {
+ struct iovec iov = { arg, sizeof(uint32_t) };
+ fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0);
+ } else {
+ ptm_setbuffersize *in_psbs = (ptm_setbuffersize *)in_buf;
+ ptm_setbuffersize out_psbs;
+ uint32_t buffersize, minsize, maxsize;
+
+ buffersize = in_psbs->u.req.buffersize;
+
+ if (buffersize > 0 && tpm_running)
+ goto error_running;
+
+ buffersize = TPMLIB_SetBufferSize(buffersize,
+ &minsize,
+ &maxsize);
+
+ out_psbs.u.resp.tpm_result = TPM_SUCCESS;
+ out_psbs.u.resp.buffersize = buffersize;
+ out_psbs.u.resp.minsize = minsize;
+ out_psbs.u.resp.maxsize = maxsize;
+ fuse_reply_ioctl(req, 0, &out_psbs, sizeof(out_psbs));
+ }
+ break;
+
+ case PTM_GET_INFO:
+ if (out_bufsz != sizeof(ptm_getinfo)) {
+ struct iovec iov = { arg, sizeof(uint32_t) };
+ fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0);
+ } else {
+ ptm_getinfo *in_pgi = (ptm_getinfo *)in_buf;
+ ptm_getinfo out_pgi;
+ char *info_data;
+ uint32_t length, offset;
+
+ info_data = TPMLIB_GetInfo(in_pgi->u.req.flags);
+ if (!info_data)
+ goto error_memory;
+
+ offset = in_pgi->u.req.offset;
+ if (offset >= strlen(info_data)) {
+ free(info_data);
+ goto error_bad_input;
+ }
+
+ length = min(strlen(info_data) + 1 - offset,
+ sizeof(out_pgi.u.resp.buffer));
+
+ out_pgi.u.resp.tpm_result = 0;
+ out_pgi.u.resp.totlength = strlen(info_data) + 1;
+ out_pgi.u.resp.length = length;
+ /* client has to collect whole string in case buffer is too small */
+ memcpy(out_pgi.u.resp.buffer, &info_data[offset], length);
+ free(info_data);
+
+ fuse_reply_ioctl(req, 0, &out_pgi, sizeof(out_pgi));
+ }
+ break;
+
default:
fuse_reply_err(req, EINVAL);
}
if (exit_prg) {
logprintf(STDOUT_FILENO, "CUSE TPM is shutting down.\n");
- pidfile_remove();
-
- exit(0);
+ ptm_cleanup();
+ fuse_session_exit(ptm_fuse_session);
}
return;
+error_bad_input:
+ res = TPM_BAD_PARAMETER;
+ fuse_reply_ioctl(req, 0, &res, sizeof(res));
+
+ goto cleanup;
+
error_running:
error_not_running:
res = TPM_BAD_ORDINAL;
fuse_reply_ioctl(req, 0, &res, sizeof(res));
+ goto cleanup;
+
+error_memory:
+ res = TPM_SIZE;
+ fuse_reply_ioctl(req, 0, &res, sizeof(res));
+
goto cleanup;
}
/* at this point the entry in /dev/ is available */
if (pidfile_write(getpid()) < 0) {
- exit(-13);
+ ret = -13;
+ goto error_exit;
}
if (param->runas) {
ret = change_process_owner(param->runas);
if (ret)
- exit(ret);
+ goto error_exit;
+ }
+
+ if (create_seccomp_profile(true, param->seccomp_action) < 0) {
+ ret = -14;
+ goto error_exit;
}
+
+ return;
+
+error_exit:
+ ptm_cleanup();
+
+ exit(ret);
+}
+
+static void ptm_cleanup(void)
+{
+ pidfile_remove();
+ log_global_free();
+ tpmstate_global_free();
+ SWTPM_NVRAM_Shutdown();
}
static const struct cuse_lowlevel_ops clops = {
.open = ptm_open,
.read = ptm_read,
.write = ptm_write,
+ .release = ptm_release,
.ioctl = ptm_ioctl,
.init_done = ptm_init_done,
};
+/* ptm_cuse_lowlevel_main is like cuse_lowlevel_main with the difference that
+ * it uses a global ptm_fuse_session so we can call fuse_session_exit() on it
+ * for a graceful exit with cleanups.
+ */
+static int
+ptm_cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci,
+ const struct cuse_lowlevel_ops *clop, void *userdata)
+{
+ int mt;
+ int ret;
+ struct cuse_param *param = userdata;
+
+ ptm_fuse_session = cuse_lowlevel_setup(argc, argv, ci, clop, &mt,
+ userdata);
+ if (ptm_fuse_session == NULL)
+ return 1;
+
+ if (param->seccomp_action == SWTPM_SECCOMP_ACTION_NONE && mt)
+ ret = fuse_session_loop_mt(ptm_fuse_session);
+ else
+ ret = fuse_session_loop(ptm_fuse_session);
+
+ cuse_lowlevel_teardown(ptm_fuse_session);
+ if (ret < 0)
+ ret = 1;
+
+ return ret;
+}
+
#ifndef HAVE_SWTPM_CUSE_MAIN
int main(int argc, char **argv)
{
{"name" , required_argument, 0, 'n'},
{"runas" , required_argument, 0, 'r'},
{"log" , required_argument, 0, 'l'},
+ {"locality" , required_argument, 0, 'L'},
{"key" , required_argument, 0, 'k'},
{"migration-key" , required_argument, 0, 'K'},
{"pid" , required_argument, 0, 'p'},
{"tpmstate" , required_argument, 0, 's'},
+ {"flags" , required_argument, 0, 'F'},
+ {"tpm2" , no_argument, 0, '2'},
{"help" , no_argument, 0, 'h'},
{"version" , no_argument, 0, 'v'},
+#ifdef WITH_SECCOMP
+ {"seccomp" , required_argument, 0, 'S'},
+#endif
+ {"print-capabilities"
+ , no_argument, 0, 'a'},
+ {"print-states" , no_argument, 0, 'e'},
{NULL , 0 , 0, 0 },
};
struct cuse_info cinfo;
- struct cuse_param param;
+ struct cuse_param param = {
+ .startupType = _TPM_ST_NONE,
+ };
const char *devname = NULL;
- char *cinfo_argv[1];
+ char *cinfo_argv[1] = { 0 };
unsigned int num;
struct passwd *passwd;
- const char *tpmdir;
+ const char *uri = NULL;
int n, tpmfd;
char path[PATH_MAX];
+ int ret = 0;
+ bool printcapabilities = false;
+ bool printstates = false;
+ bool need_init_cmd = true;
+ TPM_RESULT res;
memset(&cinfo, 0, sizeof(cinfo));
memset(¶m, 0, sizeof(param));
+ log_set_prefix("swtpm: ");
+
+ tpmversion = TPMLIB_TPM_VERSION_1_2;
+
while (true) {
opt = getopt_long(argc, argv, "M:m:n:r:hv", longopts, &longindex);
switch (opt) {
case 'M': /* major */
if (sscanf(optarg, "%u", &num) != 1) {
- fprintf(stderr, "Could not parse major number\n");
- return -1;
+ logprintf(STDERR_FILENO, "Could not parse major number\n");
+ ret = -1;
+ goto exit;
}
if (num > 65535) {
- fprintf(stderr, "Major number outside valid range [0 - 65535]\n");
- return -1;
+ logprintf(STDERR_FILENO,
+ "Major number outside valid range [0 - 65535]\n");
+ ret = -1;
+ goto exit;
}
cinfo.dev_major = num;
break;
case 'm': /* minor */
if (sscanf(optarg, "%u", &num) != 1) {
- fprintf(stderr, "Could not parse major number\n");
- return -1;
+ logprintf(STDERR_FILENO, "Could not parse major number\n");
+ ret = -1;
+ goto exit;
}
if (num > 65535) {
- fprintf(stderr, "Major number outside valid range [0 - 65535]\n");
- return -1;
+ logprintf(STDERR_FILENO,
+ "Major number outside valid range [0 - 65535]\n");
+ ret = -1;
+ goto exit;
}
cinfo.dev_minor = num;
break;
if (!cinfo.dev_info_argc) {
cinfo_argv[0] = calloc(1, strlen("DEVNAME=") + strlen(optarg) + 1);
if (!cinfo_argv[0]) {
- fprintf(stderr, "Out of memory\n");
- return -1;
+ logprintf(STDERR_FILENO, "Out of memory\n");
+ ret = -1;
+ goto exit;
}
devname = optarg;
case 's': /* tpmstate */
param.tpmstatedata = optarg;
break;
+ case 'L':
+ param.localitydata = optarg;
+ break;
+ case 'F':
+ param.flagsdata = optarg;
+ break;
+ case '2':
+ tpmversion = TPMLIB_TPM_VERSION_2;
+ break;
+ case 'S':
+ param.seccompdata = optarg;
+ break;
case 'h': /* help */
fprintf(stdout, usage, prgname, iface);
- return 0;
+ goto exit;
+ case 'a':
+ printcapabilities = true;
+ break;
+ case 'e':
+ printstates = true;
+ break;
case 'v': /* version */
fprintf(stdout, "TPM emulator CUSE interface version %d.%d.%d, "
"Copyright (c) 2014-2015 IBM Corp.\n",
SWTPM_VER_MAJOR,
SWTPM_VER_MINOR,
SWTPM_VER_MICRO);
- return 0;
+ goto exit;
}
}
- if (!cinfo.dev_info_argv) {
- fprintf(stderr, "Error: device name missing\n");
- return -2;
+ if (optind < argc) {
+ logprintf(STDERR_FILENO,
+ "Unknown parameter '%s'\n", argv[optind]);
+ ret = EXIT_FAILURE;
+ goto exit;
}
- if (handle_log_options(param.logging) < 0 ||
- handle_key_options(param.keydata) < 0 ||
- handle_migration_key_options(param.migkeydata) < 0 ||
- handle_pid_options(param.piddata) < 0 ||
- handle_tpmstate_options(param.tpmstatedata) < 0)
- return -3;
-
if (setuid(0)) {
- fprintf(stderr, "Error: Unable to setuid root. uid = %d, "
- "euid = %d, gid = %d\n", getuid(), geteuid(), getgid());
- return -4;
+ logprintf(STDERR_FILENO, "Error: Unable to setuid root. uid = %d, "
+ "euid = %d, gid = %d\n", getuid(), geteuid(), getgid());
+ ret = -4;
+ goto exit;
}
if (param.runas) {
if (!(passwd = getpwnam(param.runas))) {
- fprintf(stderr, "User '%s' does not exist\n",
- param.runas);
- return -5;
+ logprintf(STDERR_FILENO, "User '%s' does not exist\n",
+ param.runas);
+ ret = -5;
+ goto exit;
}
}
- tpmdir = tpmstate_get_dir();
- if (tpmdir == NULL) {
- fprintf(stderr,
- "Error: No TPM state directory is defined; TPM_PATH is not set\n");
- return -1;
+ if (handle_log_options(param.logging) < 0) {
+ ret = EXIT_FAILURE;
+ goto exit;
+ }
+
+ if (printcapabilities) {
+ /*
+ * Choose the TPM version so that getting/setting buffer size works.
+ * Ignore failure, for backward compatibility when TPM 1.2 is disabled.
+ */
+ ret = capabilities_print_json(true, tpmversion) ? EXIT_FAILURE : EXIT_SUCCESS;
+ goto exit;
+ }
+
+ if (tpmlib_choose_tpm_version(tpmversion) != TPM_SUCCESS) {
+ ret = EXIT_FAILURE;
+ goto exit;
+ }
+
+ tpmstate_set_version(tpmversion);
+
+ if (printstates) {
+ if (handle_tpmstate_options(param.tpmstatedata) < 0) {
+ ret = EXIT_FAILURE;
+ goto exit;
+ }
+ if (param.tpmstatedata == NULL) {
+ logprintf(STDERR_FILENO,
+ "Error: --tpmstate option is required for --print-states\n");
+ ret = EXIT_FAILURE;
+ goto exit;
+ }
+ ret = SWTPM_NVRAM_PrintJson();
+ ret = ret ? EXIT_FAILURE : EXIT_SUCCESS;
+ goto exit;
+ }
+
+ if (!cinfo.dev_info_argv) {
+ logprintf(STDERR_FILENO, "Error: device name missing\n");
+ ret = -2;
+ goto exit;
+ }
+
+ if (handle_key_options(param.keydata) < 0 ||
+ handle_migration_key_options(param.migkeydata) < 0 ||
+ handle_pid_options(param.piddata) < 0 ||
+ handle_tpmstate_options(param.tpmstatedata) < 0 ||
+ handle_seccomp_options(param.seccompdata, ¶m.seccomp_action) < 0 ||
+ handle_locality_options(param.localitydata, &locality_flags) < 0 ||
+ handle_flags_options(param.flagsdata, &need_init_cmd,
+ ¶m.startupType) < 0) {
+ ret = -3;
+ goto exit;
+ }
+
+ uri = tpmstate_get_backend_uri();
+ if (uri == NULL) {
+ logprintf(STDERR_FILENO,
+ "Error: No TPM state directory is defined; "
+ "TPM_PATH is not set\n");
+ ret = -1;
+ goto exit;
}
n = snprintf(path, sizeof(path), "/dev/%s", devname);
if (n < 0) {
- fprintf(stderr,
- "Error: Could not create device file name\n");
- return -1;
+ logprintf(STDERR_FILENO,
+ "Error: Could not create device file name\n");
+ ret = -1;
+ goto exit;
}
if (n >= (int)sizeof(path)) {
- fprintf(stderr,
- "Error: Buffer too small to create device file name\n");
- return -1;
+ logprintf(STDERR_FILENO,
+ "Error: Buffer too small to create device file name\n");
+ ret = -1;
+ goto exit;
}
tpmfd = open(path, O_RDWR);
if (tpmfd >= 0) {
close(tpmfd);
- fprintf(stderr,
- "Error: A device '%s' already exists.\n",
- path);
- return -1;
+ logprintf(STDERR_FILENO,
+ "Error: A device '%s' already exists.\n",
+ path);
+ ret = -1;
+ goto exit;
+ }
+
+ if (tpmlib_register_callbacks(&cbs) != TPM_SUCCESS) {
+ ret = -1;
+ goto exit;
}
worker_thread_init();
+ if (!need_init_cmd) {
+ if (tpm_start(0, tpmversion, &res) < 0) {
+ ret = -1;
+ goto exit;
+ }
+ tpm_running = true;
+ }
+
+ if (param.startupType != _TPM_ST_NONE) {
+ if (ptm_send_startup(param.startupType, tpmversion) < 0) {
+ ret = -1;
+ goto exit;
+ }
+ }
+
#if GLIB_MINOR_VERSION >= 32
g_mutex_init(FILE_OPS_LOCK);
#else
FILE_OPS_LOCK = g_mutex_new();
#endif
- return cuse_lowlevel_main(1, argv, &cinfo, &clops, ¶m);
+ ret = ptm_cuse_lowlevel_main(1, argv, &cinfo, &clops, ¶m);
+
+exit:
+ ptm_cleanup();
+ free(cinfo_argv[0]);
+
+ return ret;
}