#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
/* 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;
#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 *piddata;
char *tpmstatedata;
char *localitydata;
+ char *seccompdata;
+ unsigned int seccomp_action;
+ char *flagsdata;
+ uint16_t startupType;
};
/* single message to send to the worker thread */
} 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|aes-256-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|aes-256-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"
+" 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>[,mode=aes-cbc|aes-256-cbc][,format=hex|binary][,remove=[true|false]]\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|aes-256-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"
+" 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"
" 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...]\n"
-" : set the directory where the TPM's state will be written\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"
-" mode allows to set the file mode bits of the state\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";
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;
/*
* 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;
* 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, TPMLIB_TPMVersion l_tpmversion)
+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) {
goto error_del_pool;
}
- if (tpmlib_start(flags, l_tpmversion) != TPM_SUCCESS)
+ *res = tpmlib_start(flags, l_tpmversion);
+ if (*res != TPM_SUCCESS)
goto error_del_pool;
logprintf(STDOUT_FILENO,
&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)
+{
+ 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 (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;
}
}
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) {
tpmlib_process(&ptm_response, &ptm_res_len, &ptm_res_tot,
(unsigned char *)buf, ptm_req_len,
locality_flags, &locality, tpmversion);
- if (ptm_res_len)
+ if (ptm_res_len) {
+ ptm_read_offset = 0;
goto skip_process;
+ }
if (tpmlib_is_request_cancelable(l_tpmversion,
(const unsigned char*)buf,
/* 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 */
* 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:
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;
| PTM_CAP_SET_STATEBLOB
| PTM_CAP_STOP
| PTM_CAP_GET_CONFIG
- | PTM_CAP_SET_BUFFERSIZE;
+ | 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_SET_STATEBLOB
| PTM_CAP_STOP
| PTM_CAP_GET_CONFIG
- | PTM_CAP_SET_BUFFERSIZE;
+ | PTM_CAP_SET_BUFFERSIZE
+ | PTM_CAP_GET_INFO;
break;
}
fuse_reply_ioctl(req, 0, &ptm_caps, sizeof(ptm_caps));
TPMLIB_Terminate();
tpm_running = false;
- if (tpm_start(init_p->u.req.init_flags, tpmversion) < 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;
/* at this point the entry in /dev/ is available */
if (pidfile_write(getpid()) < 0) {
- ptm_cleanup();
- exit(-13);
+ ret = -13;
+ goto error_exit;
}
if (param->runas) {
ret = change_process_owner(param->runas);
- if (ret) {
- ptm_cleanup();
- exit(ret);
- }
+ if (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,
};
{
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 (mt)
+ 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);
{"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] = { 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));
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);
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",
goto exit;
}
- /*
- * choose the TPM version early so that getting/setting
- * buffer size works.
- */
- if (TPMLIB_ChooseTPMVersion(tpmversion) != TPM_SUCCESS) {
- logprintf(STDERR_FILENO,
- "Error: Could not choose TPM version.\n");
+ if (setuid(0)) {
+ 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))) {
+ logprintf(STDERR_FILENO, "User '%s' does not exist\n",
+ param.runas);
+ ret = -5;
+ goto exit;
+ }
+ }
+
+ 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;
}
- SWTPM_NVRAM_Set_TPMVersion(tpmversion);
+ 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");
goto exit;
}
- if (handle_log_options(param.logging) < 0 ||
- handle_key_options(param.keydata) < 0 ||
+ 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_locality_options(param.localitydata, &locality_flags) < 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;
}
- if (setuid(0)) {
- 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))) {
- logprintf(STDERR_FILENO, "User '%s' does not exist\n",
- param.runas);
- ret = -5;
- goto exit;
- }
- }
-
- tpmdir = tpmstate_get_dir();
- if (tpmdir == NULL) {
+ uri = tpmstate_get_backend_uri();
+ if (uri == NULL) {
logprintf(STDERR_FILENO,
"Error: No TPM state directory is defined; "
"TPM_PATH is not set\n");
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