wolfTPM/src/tpm2_swtpm.c

350 lines
8.5 KiB
C

/* tpm2_swtpm.c
*
* Copyright (C) 2006-2025 wolfSSL Inc.
*
* This file is part of wolfTPM.
*
* wolfTPM is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* wolfTPM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
/**
* This implements a subset of TPM TCP protocol as described in
* "TPM-Rev-2.0-Part-4-Supporting-Routines-01.38-code"
*
* This is intended for testing with a simulator such as
* http://ibmswtpm.sourceforge.net/ or
* https://github.com/stefanberger/swtpm
*
* See docs/SWTPM.md
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <wolftpm/tpm2_types.h>
#ifdef WOLFTPM_SWTPM
#include <wolftpm/tpm2.h>
#include <wolftpm/tpm2_swtpm.h>
#include <wolftpm/tpm2_packet.h>
#ifdef WOLFTPM_ZEPHYR
#include <zephyr/posix/unistd.h>
#include <zephyr/net/socket.h>
#elif defined(HAVE_UNISTD_H)
#include <unistd.h>
#endif
#include <errno.h>
#include <string.h>
#include <stdio.h>
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#include <wolftpm/tpm2_socket.h>
#ifndef TPM2_SWTPM_HOST
#define TPM2_SWTPM_HOST "localhost"
#endif
#ifndef TPM2_SWTPM_PORT
#define TPM2_SWTPM_PORT "2321"
#endif
static TPM_RC SwTpmTransmit(TPM2_CTX* ctx, const void* buffer, ssize_t bufSz)
{
TPM_RC rc = TPM_RC_SUCCESS;
ssize_t wrc = 0;
if (ctx == NULL || ctx->tcpCtx.fd < 0 || buffer == NULL) {
return BAD_FUNC_ARG;
}
wrc = write(ctx->tcpCtx.fd, buffer, bufSz);
if (bufSz != wrc) {
rc = TPM_RC_FAILURE;
}
#ifdef WOLFTPM_DEBUG_VERBOSE
if (wrc < 0) {
printf("Failed to send the TPM command to fd %d, got errno %d ="
"%s\n", ctx->tcpCtx.fd, errno, strerror(errno));
}
#endif
return rc;
}
static TPM_RC SwTpmReceive(TPM2_CTX* ctx, void* buffer, size_t rxSz)
{
TPM_RC rc = TPM_RC_SUCCESS;
ssize_t wrc = 0;
size_t bytes_remaining = rxSz;
char* ptr = (char*)buffer;
if (ctx == NULL || ctx->tcpCtx.fd < 0 || buffer == NULL) {
return BAD_FUNC_ARG;
}
while (bytes_remaining > 0) {
wrc = read(ctx->tcpCtx.fd, ptr, bytes_remaining);
if (wrc <= 0) {
#ifdef DEBUG_WOLFTPM
if (wrc == 0) {
printf("Failed to read from TPM socket: EOF\n");
}
else {
printf("Failed to read from TPM socket %d, got errno %d"
" = %s\n", ctx->tcpCtx.fd, errno, strerror(errno));
}
#endif
rc = TPM_RC_FAILURE;
break;
}
bytes_remaining -= wrc;
ptr += wrc;
#ifdef WOLFTPM_DEBUG_VERBOSE
printf("TPM socket received %zd waiting for %zu more\n",
wrc, bytes_remaining);
#endif
}
return rc;
}
static TPM_RC SwTpmConnect(TPM2_CTX* ctx, const char* host, const char* port)
{
TPM_RC rc = TPM_RC_FAILURE;
int s;
int fd = -1;
/* Zephyr doesnt support getaddrinfo;
* so we need to use Zephyr's socket API
*/
#ifdef WOLFTPM_ZEPHYR
struct zsock_addrinfo hints;
struct zsock_addrinfo *result, *rp;
if (ctx == NULL) {
return BAD_FUNC_ARG;
}
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
s = zsock_getaddrinfo(host, port, &hints, &result);
if (s != 0) {
// Handle error
return rc;
}
for (rp = result; rp != NULL; rp = rp->ai_next) {
fd = zsock_socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (fd < 0)
continue;
if (zsock_connect(fd, rp->ai_addr, rp->ai_addrlen) < 0) {
zsock_close(fd);
} else {
break;
}
}
zsock_freeaddrinfo(result);
if (rp != NULL) {
ctx->tcpCtx.fd = fd;
rc = TPM_RC_SUCCESS;
}
#ifdef DEBUG_WOLFTPM
else {
printf("Failed to connect to %s %s\n", host, port);
}
#endif
#else /* !WOLFTPM_ZEPHYR */
struct addrinfo hints;
struct addrinfo *result, *rp;
if (ctx == NULL) {
return BAD_FUNC_ARG;
}
XMEMSET(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
s = getaddrinfo(host, port, &hints, &result);
if (s != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
}
for (rp = result; rp != NULL; rp = rp->ai_next) {
fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (fd == -1)
continue;
if (connect(fd, rp->ai_addr, rp->ai_addrlen) == -1) {
close(fd);
}
else {
break;
}
}
freeaddrinfo(result);
if (rp != NULL) {
ctx->tcpCtx.fd = fd;
rc = TPM_RC_SUCCESS;
}
#ifdef DEBUG_WOLFTPM
else {
printf("Failed to connect to %s %s\n", host, port);
}
#endif
#endif /* WOLFTPM_ZEPHYR */
return rc;
}
static TPM_RC SwTpmDisconnect(TPM2_CTX* ctx)
{
TPM_RC rc = TPM_RC_SUCCESS;
uint32_t tss_cmd;
if (ctx == NULL || ctx->tcpCtx.fd < 0) {
return BAD_FUNC_ARG;
}
/* end swtpm session */
tss_cmd = TPM2_Packet_SwapU32(TPM_SESSION_END);
rc = SwTpmTransmit(ctx, &tss_cmd, sizeof(uint32_t));
#ifdef WOLFTPM_DEBUG_VERBOSE
if (rc != TPM_RC_SUCCESS) {
printf("Failed to transmit SESSION_END\n");
}
#endif
if (0 != close(ctx->tcpCtx.fd)) {
rc = TPM_RC_FAILURE;
#ifdef WOLFTPM_DEBUG_VERBOSE
printf("Failed to close fd %d, got errno %d ="
"%s\n", ctx->tcpCtx.fd, errno, strerror(errno));
#endif
}
ctx->tcpCtx.fd = -1;
return rc;
}
/* Talk to a TPM through socket
* return TPM_RC_SUCCESS on success,
* TPM_RC_FAILURE on other errors
*/
int TPM2_SWTPM_SendCommand(TPM2_CTX* ctx, TPM2_Packet* packet)
{
int rc = TPM_RC_FAILURE;
int rspSz = 0;
uint32_t tss_word;
if (ctx == NULL) {
return BAD_FUNC_ARG;
}
if (ctx->tcpCtx.fd < 0) {
rc = SwTpmConnect(ctx, TPM2_SWTPM_HOST, TPM2_SWTPM_PORT);
}
#ifdef WOLFTPM_DEBUG_VERBOSE
printf("Command size: %d\n", packet->pos);
TPM2_PrintBin(packet->buf, packet->pos);
#endif
/* send start */
tss_word = TPM2_Packet_SwapU32(TPM_SEND_COMMAND);
if (rc == TPM_RC_SUCCESS) {
rc = SwTpmTransmit(ctx, &tss_word, sizeof(uint32_t));
}
/* locality */
if (rc == TPM_RC_SUCCESS) {
rc = SwTpmTransmit(ctx, &ctx->locality, sizeof(uint8_t));
}
/* buffer size */
tss_word = TPM2_Packet_SwapU32(packet->pos);
if (rc == TPM_RC_SUCCESS) {
rc = SwTpmTransmit(ctx, &tss_word, sizeof(uint32_t));
}
/* Send the TPM command buffer */
if (rc == TPM_RC_SUCCESS) {
rc = SwTpmTransmit(ctx, packet->buf, packet->pos);
}
/* receive response */
if (rc == TPM_RC_SUCCESS) {
rc = SwTpmReceive(ctx, &tss_word, sizeof(uint32_t));
rspSz = TPM2_Packet_SwapU32(tss_word);
if (rspSz > packet->size) {
#ifdef WOLFTPM_DEBUG_VERBOSE
printf("Response size(%d) larger than command buffer(%d)\n",
rspSz, packet->pos);
#endif
rc = TPM_RC_FAILURE;
}
}
/* This performs a blocking read and could hang. This means a
* misbehaving actor on the other end of the socket
*/
if (rc == TPM_RC_SUCCESS) {
rc = SwTpmReceive(ctx, packet->buf, rspSz);
}
/* receive ack */
if (rc == TPM_RC_SUCCESS) {
rc = SwTpmReceive(ctx, &tss_word, sizeof(uint32_t));
tss_word = TPM2_Packet_SwapU32(tss_word);
#ifdef WOLFTPM_DEBUG
if (tss_word != 0) {
printf("SWTPM ack %d\n", tss_word);
}
#endif
}
#ifdef WOLFTPM_DEBUG_VERBOSE
if (rspSz > 0) {
printf("Response size: %d\n", rspSz);
TPM2_PrintBin(packet->buf, rspSz);
}
#endif
if (ctx->tcpCtx.fd >= 0) {
TPM_RC rc_disconnect = SwTpmDisconnect(ctx);
if (rc == TPM_RC_SUCCESS) {
rc = rc_disconnect;
}
}
return rc;
}
#endif /* WOLFTPM_SWTPM */