blob: 972fb951092d0a14f1115d4779d1f44795a4a6da [file] [log] [blame]
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2015 - 2018 Intel Corporation
* All rights reserved.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <inttypes.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef _WIN32
#include <sys/time.h>
#include <unistd.h>
#endif
#include "tss2_mu.h"
#include "tss2_tcti_mssim.h"
#include "tcti-mssim.h"
#include "tcti-common.h"
#include "util/key-value-parse.h"
#define LOGMODULE tcti
#include "util/log.h"
/*
* This function wraps the "up-cast" of the opaque TCTI context type to the
* type for the mssim TCTI context. If passed a NULL context the function
* returns a NULL ptr. The function doesn't check magic number anymore
* It should checked by the appropriate tcti_common_checks.
*/
TSS2_TCTI_MSSIM_CONTEXT*
tcti_mssim_context_cast (TSS2_TCTI_CONTEXT *tcti_ctx)
{
if (tcti_ctx == NULL)
return NULL;
return (TSS2_TCTI_MSSIM_CONTEXT*)tcti_ctx;
}
/*
* This function down-casts the mssim TCTI context to the common context
* defined in the tcti-common module.
*/
TSS2_TCTI_COMMON_CONTEXT*
tcti_mssim_down_cast (TSS2_TCTI_MSSIM_CONTEXT *tcti_mssim)
{
if (tcti_mssim == NULL) {
return NULL;
}
return &tcti_mssim->common;
}
/*
* This function is for sending one of the MS_SIM_* platform commands to the
* Microsoft TPM2 simulator. These are sent over the platform socket.
*/
TSS2_RC tcti_platform_command (
TSS2_TCTI_CONTEXT *tctiContext,
UINT32 cmd)
{
TSS2_TCTI_MSSIM_CONTEXT *tcti_mssim = tcti_mssim_context_cast (tctiContext);
uint8_t buf [sizeof (cmd)] = { 0 };
UINT32 rsp = 0;
TSS2_RC rc = TSS2_RC_SUCCESS;
int ret;
ssize_t read_ret;
if (tcti_mssim == NULL) {
return TSS2_TCTI_RC_BAD_REFERENCE;
}
if (TSS2_TCTI_MAGIC (tcti_mssim) != TCTI_MSSIM_MAGIC) {
return TSS2_TCTI_RC_BAD_CONTEXT;
}
rc = Tss2_MU_UINT32_Marshal (cmd, buf, sizeof (cmd), NULL);
if (rc != TSS2_RC_SUCCESS) {
LOG_ERROR ("Failed to marshal platform command %" PRIu32 ", rc: 0x%"
PRIx32, cmd, rc);
return rc;
}
LOGBLOB_DEBUG(buf, sizeof (cmd), "Sending %zu bytes to socket %" PRIu32
":", sizeof (cmd), tcti_mssim->platform_sock);
ret = write_all (tcti_mssim->platform_sock, buf, sizeof (cmd));
if (ret < (ssize_t) sizeof (cmd)) {
LOG_ERROR("Failed to send platform command %d with error: %d",
cmd, ret);
return TSS2_TCTI_RC_IO_ERROR;
}
#ifdef _WIN32
read_ret = recv (tcti_mssim->platform_sock, (char *) buf, sizeof (buf), 0);
if (read_ret < (ssize_t) sizeof (buf)) {
LOG_ERROR ("Failed to get response to platform command, errno %d: %s",
WSAGetLastError(), strerror (WSAGetLastError()));
return TSS2_TCTI_RC_IO_ERROR;
}
#else
read_ret = read(tcti_mssim->platform_sock, buf, sizeof (buf));
if (read_ret < (ssize_t) sizeof (buf)) {
LOG_ERROR ("Failed to get response to platform command, errno %d: %s",
errno, strerror (errno));
return TSS2_TCTI_RC_IO_ERROR;
}
#endif
LOGBLOB_DEBUG (buf, sizeof (buf), "Received %zu bytes from socket 0x%"
PRIx32 ":", read_ret, tcti_mssim->platform_sock);
rc = Tss2_MU_UINT32_Unmarshal (buf, sizeof (rsp), NULL, &rsp);
if (rc != TSS2_RC_SUCCESS) {
LOG_ERROR ("Failed to unmarshal response to platform command. rc: 0x%"
PRIx32, rc);
return rc;
}
if (rsp != 0) {
LOG_INFO ("Platform command failed with error: %" PRIu32, rsp);
return TSS2_TCTI_RC_IO_ERROR;
}
return rc;
}
/*
* This function sends the special TPM_SESSION_END message over the provided
* socket.
*/
TSS2_RC
send_sim_session_end (
SOCKET sock)
{
uint8_t buf [4] = { 0, };
TSS2_RC rc;
rc = Tss2_MU_UINT32_Marshal (TPM_SESSION_END, buf, sizeof (buf), NULL);
if (rc == TSS2_RC_SUCCESS) {
return rc;
}
return socket_xmit_buf (sock, buf, sizeof (buf));
}
/*
* This function is used to send the simulator a sort of command message
* that tells it we're about to send it a TPM command. This requires that
* we first send it a 4 byte code that's defined by the simulator. Then
* another byte identifying the locality and finally the size of the TPM
* command buffer that we're about to send. After these 9 bytes are sent
* the simulator will accept a TPM command buffer.
*/
#define SIM_CMD_SIZE (sizeof (UINT32) + sizeof (UINT8) + sizeof (UINT32))
TSS2_RC
send_sim_cmd_setup (
TSS2_TCTI_MSSIM_CONTEXT *tcti_mssim,
UINT32 size)
{
TSS2_TCTI_COMMON_CONTEXT *tcti_common = tcti_mssim_down_cast (tcti_mssim);
uint8_t buf [SIM_CMD_SIZE] = { 0 };
size_t offset = 0;
TSS2_RC rc;
rc = Tss2_MU_UINT32_Marshal (MS_SIM_TPM_SEND_COMMAND,
buf,
sizeof (buf),
&offset);
if (rc != TSS2_RC_SUCCESS) {
return rc;
}
rc = Tss2_MU_UINT8_Marshal (tcti_common->locality,
buf,
sizeof (buf),
&offset);
if (rc != TSS2_RC_SUCCESS) {
return rc;
}
rc = Tss2_MU_UINT32_Marshal (size, buf, sizeof (buf), &offset);
if (rc != TSS2_RC_SUCCESS) {
return rc;
}
return socket_xmit_buf (tcti_mssim->tpm_sock, buf, sizeof (buf));
}
TSS2_RC
tcti_mssim_transmit (
TSS2_TCTI_CONTEXT *tcti_ctx,
size_t size,
const uint8_t *cmd_buf)
{
TSS2_TCTI_MSSIM_CONTEXT *tcti_mssim = tcti_mssim_context_cast (tcti_ctx);
TSS2_TCTI_COMMON_CONTEXT *tcti_common = tcti_mssim_down_cast (tcti_mssim);
tpm_header_t header;
TSS2_RC rc;
rc = tcti_common_transmit_checks (tcti_common, cmd_buf, TCTI_MSSIM_MAGIC);
if (rc != TSS2_RC_SUCCESS) {
return rc;
}
rc = header_unmarshal (cmd_buf, &header);
if (rc != TSS2_RC_SUCCESS) {
return rc;
}
if (header.size != size) {
LOG_ERROR ("Buffer size parameter: %zu, and TPM2 command header size "
"field: %" PRIu32 " disagree.", size, header.size);
return TSS2_TCTI_RC_BAD_VALUE;
}
LOG_DEBUG ("Sending command with TPM_CC 0x%" PRIx32 " and size %" PRIu32,
header.code, header.size);
rc = send_sim_cmd_setup (tcti_mssim, header.size);
if (rc != TSS2_RC_SUCCESS) {
return rc;
}
rc = socket_xmit_buf (tcti_mssim->tpm_sock, cmd_buf, size);
if (rc != TSS2_RC_SUCCESS) {
return rc;
}
tcti_common->state = TCTI_STATE_RECEIVE;
return rc;
}
TSS2_RC
tcti_mssim_cancel (
TSS2_TCTI_CONTEXT *tctiContext)
{
TSS2_TCTI_MSSIM_CONTEXT *tcti_mssim = tcti_mssim_context_cast (tctiContext);
TSS2_TCTI_COMMON_CONTEXT *tcti_common = tcti_mssim_down_cast (tcti_mssim);
TSS2_RC rc;
rc = tcti_common_cancel_checks (tcti_common, TCTI_MSSIM_MAGIC);
if (rc != TSS2_RC_SUCCESS) {
return rc;
}
rc = tcti_platform_command (tctiContext, MS_SIM_CANCEL_ON);
if (rc != TSS2_RC_SUCCESS) {
return rc;
}
tcti_common->state = TCTI_STATE_TRANSMIT;
tcti_mssim->cancel = 1;
return rc;
}
TSS2_RC
tcti_mssim_set_locality (
TSS2_TCTI_CONTEXT *tctiContext,
uint8_t locality)
{
TSS2_TCTI_MSSIM_CONTEXT *tcti_mssim = tcti_mssim_context_cast (tctiContext);
TSS2_TCTI_COMMON_CONTEXT *tcti_common = tcti_mssim_down_cast (tcti_mssim);
TSS2_RC rc;
rc = tcti_common_set_locality_checks (tcti_common, TCTI_MSSIM_MAGIC);
if (rc != TSS2_RC_SUCCESS) {
return rc;
}
tcti_common->locality = locality;
return TSS2_RC_SUCCESS;
}
TSS2_RC
tcti_mssim_get_poll_handles (
TSS2_TCTI_CONTEXT *tctiContext,
TSS2_TCTI_POLL_HANDLE *handles,
size_t *num_handles)
{
(void)(tctiContext);
(void)(handles);
(void)(num_handles);
return TSS2_TCTI_RC_NOT_IMPLEMENTED;
}
void
tcti_mssim_finalize(
TSS2_TCTI_CONTEXT *tctiContext)
{
TSS2_TCTI_MSSIM_CONTEXT *tcti_mssim = tcti_mssim_context_cast (tctiContext);
if (tcti_mssim == NULL) {
return;
}
send_sim_session_end (tcti_mssim->platform_sock);
send_sim_session_end (tcti_mssim->tpm_sock);
socket_close (&tcti_mssim->platform_sock);
socket_close (&tcti_mssim->tpm_sock);
}
TSS2_RC
tcti_mssim_receive (
TSS2_TCTI_CONTEXT *tctiContext,
size_t *response_size,
unsigned char *response_buffer,
int32_t timeout)
{
#ifdef TEST_FAPI_ASYNC
/* Used for simulating a timeout. */
static int wait = 0;
#endif
TSS2_TCTI_MSSIM_CONTEXT *tcti_mssim = tcti_mssim_context_cast (tctiContext);
TSS2_TCTI_COMMON_CONTEXT *tcti_common = tcti_mssim_down_cast (tcti_mssim);
TSS2_RC rc;
UINT32 trash;
int ret;
rc = tcti_common_receive_checks (tcti_common,
response_size,
TCTI_MSSIM_MAGIC);
if (rc != TSS2_RC_SUCCESS) {
return rc;
}
if (timeout != TSS2_TCTI_TIMEOUT_BLOCK) {
LOG_TRACE("Asynchronous I/O not actually implemented.");
#ifdef TEST_FAPI_ASYNC
if (wait < 1) {
LOG_TRACE("Simulating Async by requesting another invocation.");
wait += 1;
return TSS2_TCTI_RC_TRY_AGAIN;
} else {
LOG_TRACE("Sending the actual result.");
wait = 0;
}
#endif /* TEST_FAPI_ASYNC */
}
if (tcti_common->header.size == 0) {
/* Receive the size of the response. */
uint8_t size_buf [sizeof (UINT32)];
ret = socket_recv_buf (tcti_mssim->tpm_sock, size_buf, sizeof (UINT32));
if (ret != sizeof (UINT32)) {
rc = TSS2_TCTI_RC_IO_ERROR;
goto out;
}
rc = Tss2_MU_UINT32_Unmarshal (size_buf,
sizeof (size_buf),
0,
&tcti_common->header.size);
if (rc != TSS2_RC_SUCCESS) {
LOG_WARNING ("Failed to unmarshal size from tpm2 simulator "
"protocol: 0x%" PRIu32, rc);
goto out;
}
LOG_DEBUG ("response size: %" PRIu32, tcti_common->header.size);
}
if (response_buffer == NULL) {
*response_size = tcti_common->header.size;
return TSS2_RC_SUCCESS;
}
if (*response_size < tcti_common->header.size) {
*response_size = tcti_common->header.size;
LOG_ERROR("Response size to big: %zu > %u", *response_size, tcti_common->header.size);
return TSS2_TCTI_RC_INSUFFICIENT_BUFFER;
}
*response_size = tcti_common->header.size;
/* Receive the TPM response. */
LOG_DEBUG ("Reading response of size %" PRIu32, tcti_common->header.size);
ret = socket_recv_buf (tcti_mssim->tpm_sock,
(unsigned char *)response_buffer,
tcti_common->header.size);
if (ret < (ssize_t)tcti_common->header.size) {
rc = TSS2_TCTI_RC_IO_ERROR;
goto out;
}
LOGBLOB_DEBUG(response_buffer, tcti_common->header.size,
"Response buffer received:");
/* Receive the appended four bytes of 0's */
ret = socket_recv_buf (tcti_mssim->tpm_sock,
(unsigned char *)&trash,
4);
if (ret != 4) {
rc = TSS2_TCTI_RC_IO_ERROR;
goto out;
}
if (tcti_mssim->cancel) {
rc = tcti_platform_command (tctiContext, MS_SIM_CANCEL_OFF);
tcti_mssim->cancel = 0;
}
/*
* Executing code beyond this point transitions the state machine to
* TRANSMIT. Another call to this function will not be possible until
* another command is sent to the TPM.
*/
out:
tcti_common->header.size = 0;
tcti_common->state = TCTI_STATE_TRANSMIT;
return rc;
}
/**
* This function sends the Microsoft simulator the MS_SIM_POWER_ON and
* MS_SIM_NV_ON commands using the platform command mechanism. Without
* these the simulator will respond with zero sized buffer which causes
* the TSS to freak out. Sending this command more than once is harmless,
* so it's advisable to call this function as part of the TCTI context
* initialization just to be sure.
*
* NOTE: The caller will still need to call Tss2_Sys_Startup. If they
* don't, an error will be returned from each call till they do but
* the error will at least be meaningful (TPM2_RC_INITIALIZE).
*/
static TSS2_RC
simulator_setup (
TSS2_TCTI_CONTEXT *tctiContext)
{
TSS2_RC rc;
LOG_TRACE ("Initializing TCTI context 0x%" PRIxPTR,
(uintptr_t)tctiContext);
rc = tcti_platform_command (tctiContext, MS_SIM_POWER_ON);
if (rc != TSS2_RC_SUCCESS) {
LOG_WARNING ("Failed to send MS_SIM_POWER_ON platform command.");
return rc;
}
rc = tcti_platform_command (tctiContext, MS_SIM_NV_ON);
if (rc != TSS2_RC_SUCCESS) {
LOG_WARNING ("Failed to send MS_SIM_NV_ON platform command.");
}
return rc;
}
/*
* This is a utility function to extract a TCP port number from a string.
* The string must be 6 characters long. If the supplied string contains an
* invalid port number then 0 is returned.
*/
static uint16_t
string_to_port (char port_str[6])
{
uint32_t port = 0;
if (sscanf (port_str, "%" SCNu32, &port) == EOF || port > UINT16_MAX) {
return 0;
}
return port;
}
/*
* This function is a callback conforming to the KeyValueFunc prototype. It
* is called by the key-value-parse module for each key / value pair extracted
* from the configuration string. Its sole purpose is to identify valid keys
* from the conf string and to store their corresponding values in the
* mssim_conf_t structure which is passed through the 'user_data' parameter.
*/
TSS2_RC
mssim_kv_callback (const key_value_t *key_value,
void *user_data)
{
mssim_conf_t *mssim_conf = (mssim_conf_t*)user_data;
LOG_TRACE ("key_value: 0x%" PRIxPTR " and user_data: 0x%" PRIxPTR,
(uintptr_t)key_value, (uintptr_t)user_data);
if (key_value == NULL || user_data == NULL) {
LOG_WARNING ("%s passed NULL parameter", __func__);
return TSS2_TCTI_RC_GENERAL_FAILURE;
}
LOG_DEBUG ("key: %s / value: %s\n", key_value->key, key_value->value);
if (strcmp (key_value->key, "host") == 0) {
mssim_conf->host = key_value->value;
return TSS2_RC_SUCCESS;
} else if (strcmp (key_value->key, "port") == 0) {
mssim_conf->port = string_to_port (key_value->value);
if (mssim_conf->port == 0) {
return TSS2_TCTI_RC_BAD_VALUE;
}
return TSS2_RC_SUCCESS;
} else {
return TSS2_TCTI_RC_BAD_VALUE;
}
}
void
tcti_mssim_init_context_data (
TSS2_TCTI_COMMON_CONTEXT *tcti_common)
{
TSS2_TCTI_MAGIC (tcti_common) = TCTI_MSSIM_MAGIC;
TSS2_TCTI_VERSION (tcti_common) = TCTI_VERSION;
TSS2_TCTI_TRANSMIT (tcti_common) = tcti_mssim_transmit;
TSS2_TCTI_RECEIVE (tcti_common) = tcti_mssim_receive;
TSS2_TCTI_FINALIZE (tcti_common) = tcti_mssim_finalize;
TSS2_TCTI_CANCEL (tcti_common) = tcti_mssim_cancel;
TSS2_TCTI_GET_POLL_HANDLES (tcti_common) = tcti_mssim_get_poll_handles;
TSS2_TCTI_SET_LOCALITY (tcti_common) = tcti_mssim_set_locality;
TSS2_TCTI_MAKE_STICKY (tcti_common) = tcti_make_sticky_not_implemented;
tcti_common->state = TCTI_STATE_TRANSMIT;
tcti_common->locality = 0;
memset (&tcti_common->header, 0, sizeof (tcti_common->header));
}
/*
* This is an implementation of the standard TCTI initialization function for
* this module.
*/
TSS2_RC
Tss2_Tcti_Mssim_Init (
TSS2_TCTI_CONTEXT *tctiContext,
size_t *size,
const char *conf)
{
TSS2_TCTI_MSSIM_CONTEXT *tcti_mssim = (TSS2_TCTI_MSSIM_CONTEXT*)tctiContext;
TSS2_TCTI_COMMON_CONTEXT *tcti_common = tcti_mssim_down_cast (tcti_mssim);
TSS2_RC rc;
char *conf_copy = NULL;
mssim_conf_t mssim_conf = MSSIM_CONF_DEFAULT_INIT;
if (conf == NULL) {
LOG_TRACE ("tctiContext: 0x%" PRIxPTR ", size: 0x%" PRIxPTR ""
" default configuration will be used.",
(uintptr_t)tctiContext, (uintptr_t)size);
} else {
LOG_TRACE ("tctiContext: 0x%" PRIxPTR ", size: 0x%" PRIxPTR ", conf: %s",
(uintptr_t)tctiContext, (uintptr_t)size, conf);
}
if (size == NULL) {
return TSS2_TCTI_RC_BAD_VALUE;
}
if (tctiContext == NULL) {
*size = sizeof (TSS2_TCTI_MSSIM_CONTEXT);
return TSS2_RC_SUCCESS;
}
if (conf != NULL) {
LOG_TRACE ("conf is not NULL");
if (strlen (conf) > TCTI_MSSIM_CONF_MAX) {
LOG_WARNING ("Provided conf string exceeds maximum of %u",
TCTI_MSSIM_CONF_MAX);
return TSS2_TCTI_RC_BAD_VALUE;
}
conf_copy = strdup (conf);
if (conf_copy == NULL) {
LOG_ERROR ("Failed to allocate buffer: %s", strerror (errno));
rc = TSS2_TCTI_RC_GENERAL_FAILURE;
goto fail_out;
}
LOG_DEBUG ("Dup'd conf string to: 0x%" PRIxPTR,
(uintptr_t)conf_copy);
rc = parse_key_value_string (conf_copy,
mssim_kv_callback,
&mssim_conf);
if (rc != TSS2_RC_SUCCESS) {
goto fail_out;
}
}
LOG_DEBUG ("Initializing mssim TCTI with host: %s, port: %" PRIu16,
mssim_conf.host, mssim_conf.port);
tcti_mssim->tpm_sock = -1;
tcti_mssim->platform_sock = -1;
rc = socket_connect (mssim_conf.host,
mssim_conf.port,
&tcti_mssim->tpm_sock);
if (rc != TSS2_RC_SUCCESS) {
goto fail_out;
}
rc = socket_connect (mssim_conf.host,
mssim_conf.port + 1,
&tcti_mssim->platform_sock);
if (rc != TSS2_RC_SUCCESS) {
goto fail_out;
}
tcti_mssim_init_context_data (tcti_common);
rc = simulator_setup (tctiContext);
if (rc != TSS2_RC_SUCCESS) {
goto fail_out;
}
if (conf_copy != NULL) {
free (conf_copy);
}
return TSS2_RC_SUCCESS;
fail_out:
if (conf_copy != NULL) {
free (conf_copy);
}
socket_close (&tcti_mssim->tpm_sock);
socket_close (&tcti_mssim->platform_sock);
return rc;
}
/* public info structure */
const TSS2_TCTI_INFO tss2_tcti_info = {
.version = TCTI_VERSION,
.name = "tcti-socket",
.description = "TCTI module for communication with the Microsoft TPM2 Simulator.",
.config_help = "Key / value string in the form \"host=localhost,port=2321\".",
.init = Tss2_Tcti_Mssim_Init,
};
const TSS2_TCTI_INFO*
Tss2_Tcti_Info (void)
{
return &tss2_tcti_info;
}