Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 109 additions & 13 deletions src/fwtpm/fwtpm_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@
#ifndef _WIN32
#include <sys/select.h>
#include <arpa/inet.h>
#include <netinet/tcp.h> /* TCP_NODELAY */
#endif

/* Scatter/gather send, so one mssim reply is one segment */
#if !defined(_WIN32) && !defined(WOLFTPM_ZEPHYR) && \
!defined(FWTPM_NO_WRITEV)
#define FWTPM_HAVE_WRITEV
#include <sys/uio.h>
#endif

/* Async-signal-safe stop flag. Set by FWTPM_IO_RequestStop() (callable from
Expand Down Expand Up @@ -145,6 +153,49 @@ static int SocketSend(FWTPM_CTX* ctx, SOCKET_T fd, const void* buf, int sz)
return TPM_RC_SUCCESS;
}

#ifdef FWTPM_HAVE_WRITEV
/* Send an iovec set, advancing over partial writes. Modifies iov. */
static int SocketSendVec(FWTPM_CTX* ctx, SOCKET_T fd, struct iovec* iov,
int cnt)
{
ssize_t sent;
int i = 0;

while (i < cnt) {
int rc = FWTPM_Clock_Update(ctx);
if (rc != TPM_RC_SUCCESS && rc != TPM_RC_NV_RATE) return rc;
rc = SocketWait(fd, 1);
if (rc == 0) {
if (g_stopRequested) return TPM_RC_FAILURE;
continue;
}
sent = (rc < 0) ? -1 : writev(fd, &iov[i], cnt - i);
if (sent < 0) {
if (errno == EINTR) {
continue;
}
#ifdef DEBUG_WOLFTPM
printf("fwTPM: writev error %d\n", errno);
#endif
return TPM_RC_FAILURE;
}
if (sent == 0) {
/* No progress on a blocking socket with bytes outstanding */
return TPM_RC_FAILURE;
}
while (i < cnt && (size_t)sent >= iov[i].iov_len) {
sent -= (ssize_t)iov[i].iov_len;
i++;
}
if (i < cnt && sent > 0) {
iov[i].iov_base = (char*)iov[i].iov_base + sent;
iov[i].iov_len -= (size_t)sent;
}
}
return TPM_RC_SUCCESS;
}
#endif /* FWTPM_HAVE_WRITEV */

static int SocketRecv(FWTPM_CTX* ctx, SOCKET_T fd, void* buf, int sz)
{
char* ptr = (char*)buf;
Expand Down Expand Up @@ -260,6 +311,61 @@ static SOCKET_T CreateListenSocket(int port)
return fd;
}

/* Disable Nagle, which would hold the small mssim acknowledgement until the
* peer's delayed ACK. Advisory: a failure costs latency, not correctness. */
static void SetSocketNoDelay(SOCKET_T fd)
{
#ifdef TCP_NODELAY
int optval = 1;

/* Winsock takes a char* here where POSIX takes a void*. */
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
(const char*)&optval, sizeof(optval)) != 0) {
#ifdef DEBUG_WOLFTPM
printf("fwTPM: setsockopt(TCP_NODELAY) failed\n");
#endif
}
#else
(void)fd;
#endif
}

/* mssim reply: size(4) + response + ack(4), as one segment where the
* platform allows it. A custom HAL has no vectored form, so it keeps the
* three sequential sends. */
static int SendMssimReply(FWTPM_CTX* ctx, SOCKET_T clientFd,
const byte* rsp, int rspSize)
{
int rc;
UINT32 sizeVal = FwTpmSwapU32((UINT32)rspSize);
UINT32 ackVal = FwTpmSwapU32(0); /* ack = 0 (success) */
#ifdef FWTPM_HAVE_WRITEV
struct iovec iov[3];

if (ctx->ioHal.wait == NULL && clientFd != FWTPM_INVALID_FD) {
iov[0].iov_base = &sizeVal;
iov[0].iov_len = sizeof(sizeVal);
iov[1].iov_base = (void*)rsp;
iov[1].iov_len = (size_t)rspSize;
iov[2].iov_base = &ackVal;
iov[2].iov_len = sizeof(ackVal);
return SocketSendVec(ctx, clientFd, iov, 3);
}
#endif

rc = TransportSend(ctx, FWTPM_IO_TYPE_COMMAND, clientFd, &sizeVal,
(int)sizeof(sizeVal));
if (rc == TPM_RC_SUCCESS) {
rc = TransportSend(ctx, FWTPM_IO_TYPE_COMMAND, clientFd, rsp,
rspSize);
}
if (rc == TPM_RC_SUCCESS) {
rc = TransportSend(ctx, FWTPM_IO_TYPE_COMMAND, clientFd, &ackVal,
(int)sizeof(ackVal));
}
return rc;
}

/* Build a minimal TPM error response using TPM2_Packet API */
static int BuildErrorResponse(byte* rspBuf, UINT16 tag, TPM_RC rc)
{
Expand Down Expand Up @@ -428,7 +534,6 @@ static int DispatchAndRespond(FWTPM_CTX* ctx, UINT32 cmdSize, int locality,
int rc;
int rspSize = 0;
int procRc;
UINT32 netVal;
int dispatched = 0;
#ifdef WOLFTPM_SPDM_RESPONDER
UINT16 firstTag = (cmdSize >= 2) ? FwLoadU16BE(ctx->cmdBuf) : 0;
Expand Down Expand Up @@ -494,18 +599,7 @@ static int DispatchAndRespond(FWTPM_CTX* ctx, UINT32 cmdSize, int locality,
ctx->rspBuf, rspSize);
}
else {
/* mssim protocol: size(4) + response + ack(4) */
netVal = FwTpmSwapU32((UINT32)rspSize);
rc = TransportSend(ctx, FWTPM_IO_TYPE_COMMAND, clientFd, &netVal, 4);
if (rc == TPM_RC_SUCCESS) {
rc = TransportSend(ctx, FWTPM_IO_TYPE_COMMAND, clientFd,
ctx->rspBuf, rspSize);
}
if (rc == TPM_RC_SUCCESS) {
netVal = FwTpmSwapU32(0); /* ack = 0 (success) */
rc = TransportSend(ctx, FWTPM_IO_TYPE_COMMAND, clientFd,
&netVal, 4);
}
rc = SendMssimReply(ctx, clientFd, ctx->rspBuf, rspSize);
}

return rc;
Expand Down Expand Up @@ -974,6 +1068,7 @@ int FWTPM_IO_ServerLoop(FWTPM_CTX* ctx)
if (FD_ISSET(ctx->io.platListenFd, &readFds)) {
SOCKET_T newFd = accept(ctx->io.platListenFd, NULL, NULL);
if (newFd != FWTPM_INVALID_FD) {
SetSocketNoDelay(newFd);
if (platFd != FWTPM_INVALID_FD) {
#ifdef DEBUG_WOLFTPM
printf("fwTPM: platform connection replaced\n");
Expand Down Expand Up @@ -1008,6 +1103,7 @@ int FWTPM_IO_ServerLoop(FWTPM_CTX* ctx)
if (FD_ISSET(ctx->io.listenFd, &readFds)) {
SOCKET_T newFd = accept(ctx->io.listenFd, NULL, NULL);
if (newFd != FWTPM_INVALID_FD) {
SetSocketNoDelay(newFd);
for (i = 0; i < FWTPM_MAX_COMMAND_CLIENTS; i++) {
if (cmdFds[i] == FWTPM_INVALID_FD) {
cmdFds[i] = newFd;
Expand Down
9 changes: 9 additions & 0 deletions tests/fwtpm_io_test_adapter.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,12 @@ int FWTPM_IO_TestHandleMssimSignal(FWTPM_CTX* ctx, SOCKET_T clientFd,
{
return HandleMssimSignal(ctx, clientFd, tssCmd);
}

int FWTPM_IO_TestSendMssimReply(FWTPM_CTX* ctx, SOCKET_T clientFd,
const byte* rsp, int rspSize);

int FWTPM_IO_TestSendMssimReply(FWTPM_CTX* ctx, SOCKET_T clientFd,
const byte* rsp, int rspSize)
{
return SendMssimReply(ctx, clientFd, rsp, rspSize);
}
47 changes: 47 additions & 0 deletions tests/fwtpm_unit_tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@
#ifdef WOLFTPM_FWTPM_UNIT_TEST
int FWTPM_IO_TestHandleMssimSignal(FWTPM_CTX* ctx, SOCKET_T clientFd,
UINT32 tssCmd);
int FWTPM_IO_TestSendMssimReply(FWTPM_CTX* ctx, SOCKET_T clientFd,
const byte* rsp, int rspSize);
#endif

#if defined(WOLFTPM_FWTPM_UNIT_TEST) && defined(HAVE_HASHDRBG)
Expand Down Expand Up @@ -20399,6 +20401,50 @@ static void test_fwtpm_mssim_hash_data_framing(void)
fwtpm_pass("MSSIM unsupported hash-data framing:", 0);
}

/* An mssim reply is size(4) + response + ack(4). Whether it goes out as one
* vectored write or three sequential ones, the byte stream on the wire must
* be identical and in that order. Builds with FWTPM_NO_WRITEV exercise the
* sequential path through this same test. */
static void test_fwtpm_mssim_reply_framing(void)
{
FWTPM_CTX ctx;
byte rsp[2048];
byte wire[4 + sizeof(rsp) + 4];
int sockets[2];
size_t total = 4 + sizeof(rsp) + 4;
size_t got = 0;
ssize_t n;
word32 i;

XMEMSET(&ctx, 0, sizeof(ctx));
XMEMSET(wire, 0, sizeof(wire));
for (i = 0; i < (word32)sizeof(rsp); i++) {
rsp[i] = (byte)(i & 0xFF);
}

AssertIntEQ(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets), 0);
AssertIntEQ(FWTPM_IO_TestSendMssimReply(&ctx, sockets[0], rsp,
(int)sizeof(rsp)), TPM_RC_SUCCESS);

while (got < total) {
n = read(sockets[1], wire + got, total - got);
if (n <= 0) {
break;
}
got += (size_t)n;
}
AssertIntEQ((int)got, (int)total);

/* size, then the response bytes unchanged, then a zero ack */
AssertIntEQ((int)GetU32BE(wire), (int)sizeof(rsp));
AssertIntEQ(XMEMCMP(wire + 4, rsp, sizeof(rsp)), 0);
AssertIntEQ((int)GetU32BE(wire + 4 + sizeof(rsp)), 0);

(void)close(sockets[1]);
(void)close(sockets[0]);
fwtpm_pass("MSSIM reply framing:", 0);
}

static void test_fwtpm_mssim_invalid_fd(void)
{
FWTPM_CTX ctx;
Expand Down Expand Up @@ -22632,6 +22678,7 @@ int fwtpm_unit_tests(int argc, char *argv[])

#if defined(WOLFTPM_FWTPM_UNIT_TEST) && !defined(_WIN32)
test_fwtpm_mssim_hash_data_framing();
test_fwtpm_mssim_reply_framing();
test_fwtpm_mssim_invalid_fd();
#endif
#ifdef WOLFTPM_FWTPM_UNIT_TEST
Expand Down
Loading