From 19e648442b49e3749a78b46f494a483cda97070f Mon Sep 17 00:00:00 2001 From: David Garske Date: Fri, 17 Apr 2026 13:00:00 -0700 Subject: [PATCH] Add network namespace isolation, macOS CI, and Windows build support for fwTPM Use unshare --user --net to isolate fwtpm_check.sh TCP ports in CI, eliminating flaky port conflicts on shared runners. Add macOS CI with socket transport tests. Add Windows socket portability (Winsock2) and build-only CI via CMake. --- .github/workflows/cmake-build.yml | 64 +++++++++-- .github/workflows/fwtpm-test.yml | 93 +++++++++++---- .github/workflows/sanitizer.yml | 8 +- CMakeLists.txt | 19 +++- configure.ac | 10 +- scripts/tpm2_tools_test.sh | 20 +++- src/fwtpm/fwtpm_command.c | 14 ++- src/fwtpm/fwtpm_io.c | 183 ++++++++++++++++++------------ src/fwtpm/fwtpm_main.c | 7 ++ src/tpm2_swtpm.c | 2 +- tests/fwtpm_check.sh | 21 ++-- wolftpm/fwtpm/fwtpm.h | 13 ++- 12 files changed, 327 insertions(+), 127 deletions(-) diff --git a/.github/workflows/cmake-build.yml b/.github/workflows/cmake-build.yml index 6c91219d..56b0a469 100644 --- a/.github/workflows/cmake-build.yml +++ b/.github/workflows/cmake-build.yml @@ -9,90 +9,123 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: ${{ matrix.config.os }} strategy: fail-fast: false matrix: config: # Default configuration (SWTPM first) - name: "Defaults" + os: ubuntu-latest options: "" # ST33 supports both SPI and I2C - name: "Module ST33 SPI" + os: ubuntu-latest options: "-DWOLFTPM_INTERFACE=SPI -DWOLFTPM_MODULE=st33" - name: "Module ST33 I2C" + os: ubuntu-latest options: "-DWOLFTPM_INTERFACE=I2C -DWOLFTPM_MODULE=st33" # ST33 Firmware - name: "Module ST33 Firmware" + os: ubuntu-latest options: "-DWOLFTPM_MODULE=st33 -DWOLFTPM_FIRMWARE=yes" # Other modules use SPI - name: "Module Microchip" + os: ubuntu-latest options: "-DWOLFTPM_INTERFACE=SPI -DWOLFTPM_MODULE=microchip" - name: "Module Nuvoton" + os: ubuntu-latest options: "-DWOLFTPM_INTERFACE=SPI -DWOLFTPM_MODULE=nuvoton" - name: "Module SLB9670" + os: ubuntu-latest options: "-DWOLFTPM_INTERFACE=SPI -DWOLFTPM_MODULE=slb9670" - name: "Module SLB9672" + os: ubuntu-latest options: "-DWOLFTPM_INTERFACE=SPI -DWOLFTPM_MODULE=slb9672" # SLB9673 is I2C - name: "Module SLB9673 I2C" + os: ubuntu-latest options: "-DWOLFTPM_INTERFACE=I2C -DWOLFTPM_MODULE=slb9673" # Test wrapper disabled - name: "No Wrapper" + os: ubuntu-latest options: "-DWOLFTPM_WRAPPER=no" # Test I2C support (enables ADV_IO automatically) - name: "I2C Enabled (legacy)" + os: ubuntu-latest options: "-DWOLFTPM_I2C=yes" # Test interface options - name: "Interface I2C" + os: ubuntu-latest options: "-DWOLFTPM_INTERFACE=I2C" - name: "Interface SPI" + os: ubuntu-latest options: "-DWOLFTPM_INTERFACE=SPI" # Test Advanced IO - name: "Advanced IO" + os: ubuntu-latest options: "-DWOLFTPM_INTERFACE=SPI -DWOLFTPM_ADVIO=yes" - name: "Advanced IO I2C" + os: ubuntu-latest options: "-DWOLFTPM_INTERFACE=I2C -DWOLFTPM_ADVIO=yes" # Test MMIO (enables ADV_IO automatically) - name: "MMIO Enabled" + os: ubuntu-latest options: "-DWOLFTPM_MMIO=yes" # Test Check Wait State - name: "Check Wait State Enabled" + os: ubuntu-latest options: "-DWOLFTPM_INTERFACE=SPI -DWOLFTPM_CHECK_WAIT_STATE=yes" - name: "Check Wait State Disabled" + os: ubuntu-latest options: "-DWOLFTPM_INTERFACE=SPI -DWOLFTPM_CHECK_WAIT_STATE=no" # Test TIS Lock - name: "TIS Lock Enabled" + os: ubuntu-latest options: "-DWOLFTPM_INTERFACE=SPI -DWOLFTPM_TIS_LOCK=yes" # Test Small Stack - name: "Small Stack" + os: ubuntu-latest options: "-DWOLFTPM_INTERFACE=SPI -DWOLFTPM_SMALL_STACK=yes" # Test HAL disabled - name: "No HAL" + os: ubuntu-latest options: "-DWOLFTPM_HAL=no" # Test Firmware disabled - name: "No Firmware" + os: ubuntu-latest options: "-DWOLFTPM_FIRMWARE=no" # Test Debug modes - name: "Debug Verbose" + os: ubuntu-latest options: "-DWOLFTPM_DEBUG=verbose" - name: "Debug IO" + os: ubuntu-latest options: "-DWOLFTPM_DEBUG=io" # Test Examples disabled - name: "No Examples" + os: ubuntu-latest options: "-DWOLFTPM_EXAMPLES=no" # Test combination of options - name: "Combined Options" + os: ubuntu-latest options: "-DWOLFTPM_INTERFACE=I2C -DWOLFTPM_MODULE=st33 -DWOLFTPM_ADVIO=yes -DWOLFTPM_CHECK_WAIT_STATE=yes" # fwTPM server with socket transport - name: "fwTPM Socket" + os: ubuntu-latest options: "-DWOLFTPM_FWTPM=yes -DWOLFTPM_INTERFACE=SWTPM" # fwTPM server with TIS/shared-memory transport - name: "fwTPM TIS" + os: ubuntu-latest options: "-DWOLFTPM_FWTPM=yes -DWOLFTPM_INTERFACE=SPI" # fwTPM server-only mode (no client library or examples) - name: "fwTPM Only" + os: ubuntu-latest options: "-DWOLFTPM_FWTPM_ONLY=yes -DWOLFTPM_INTERFACE=SWTPM" + # fwTPM socket on Windows (build-only) + - name: "fwTPM Socket (Windows)" + os: windows-latest + options: "-DWOLFTPM_FWTPM=yes -DWOLFTPM_INTERFACE=SWTPM" steps: #pull wolfTPM @@ -100,6 +133,7 @@ jobs: # Install cmake - name: Install cmake + if: runner.os == 'Linux' run: | sudo apt-get update sudo apt-get install -y cmake @@ -112,25 +146,39 @@ jobs: path: wolfssl - name: Build wolfssl working-directory: ./wolfssl + shell: bash run: | mkdir build cd build # wolfSSL PR 7188 broke "make install" unless WOLFSSL_INSTALL is set - cmake -DWOLFSSL_TPM=yes -DWOLFSSL_INSTALL=yes -DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/install" -DCMAKE_C_FLAGS="-DWC_RSA_NO_PADDING" .. - cmake --build . - cmake --install . + cmake -DWOLFSSL_TPM=yes -DWOLFSSL_INSTALL=yes \ + -DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/install" \ + -DCMAKE_C_FLAGS="-DWC_RSA_NO_PADDING -DWOLFSSL_PUBLIC_MP" .. + cmake --build . --config Release + cmake --install . --config Release #build wolftpm - name: Build wolfTPM (${{ matrix.config.name }}) + shell: bash run: | mkdir build cd build - cmake ${{ matrix.config.options }} -DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/install" -DWITH_WOLFSSL="$GITHUB_WORKSPACE/install" .. - cmake --build . - cmake --install . + cmake ${{ matrix.config.options }} \ + -DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/install" \ + -DWITH_WOLFSSL="$GITHUB_WORKSPACE/install" .. + cmake --build . --config Release + cmake --install . --config Release - name: Test fwTPM if: contains(matrix.config.options, 'WOLFTPM_FWTPM') + shell: bash run: | cd build - LD_LIBRARY_PATH="$GITHUB_WORKSPACE/install/lib" ctest --output-on-failure + if [ "$RUNNER_OS" = "Windows" ]; then + # Windows: DLL search uses PATH, and wolfssl.dll installs to install/bin + PATH="$GITHUB_WORKSPACE/install/bin:$PATH" \ + ctest -C Release --output-on-failure + else + LD_LIBRARY_PATH="$GITHUB_WORKSPACE/install/lib" \ + ctest -C Release --output-on-failure + fi diff --git a/.github/workflows/fwtpm-test.yml b/.github/workflows/fwtpm-test.yml index 19a0470a..52b57c69 100644 --- a/.github/workflows/fwtpm-test.yml +++ b/.github/workflows/fwtpm-test.yml @@ -11,37 +11,42 @@ jobs: # make check — unit tests + examples against fwtpm_server # ---------------------------------------------------------------- fwtpm-examples: - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: include: # fwTPM with socket/swtpm transport - name: fwtpm-socket + os: ubuntu-latest wolftpm_config: --enable-fwtpm --enable-swtpm --enable-debug wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen build_only: false # fwTPM with TIS/shared-memory transport - name: fwtpm-tis + os: ubuntu-latest wolftpm_config: --enable-fwtpm --enable-debug wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen build_only: false # Build-only: fwTPM with RSA disabled - name: fwtpm-no-rsa + os: ubuntu-latest wolftpm_config: --enable-fwtpm --enable-swtpm wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen --disable-rsa build_only: true # Build-only: fwTPM with ECC disabled - name: fwtpm-no-ecc + os: ubuntu-latest wolftpm_config: --enable-fwtpm --enable-swtpm wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen --disable-ecc build_only: true # Build-only: fwTPM with SHA-384 disabled - name: fwtpm-no-sha384 + os: ubuntu-latest wolftpm_config: --enable-fwtpm --enable-swtpm wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen --disable-sha384 build_only: true @@ -49,6 +54,7 @@ jobs: # Build-only: fwTPM with SHA-1 disabled (verifies !NO_SHA gating # for SHA-1 PCR bank and RSA hash conversion) - name: fwtpm-no-sha1 + os: ubuntu-latest wolftpm_config: --enable-fwtpm --enable-swtpm wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen --disable-sha build_only: true @@ -56,12 +62,14 @@ jobs: # Build-only: fwTPM server only (no client library) - name: fwtpm-only + os: ubuntu-latest wolftpm_config: --enable-fwtpm-only --enable-swtpm wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen build_only: true # Build-only: fwTPM with attestation and NV disabled - name: fwtpm-minimal + os: ubuntu-latest wolftpm_config: --enable-fwtpm --enable-swtpm wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen build_only: true @@ -69,36 +77,42 @@ jobs: # Build-only: individual FWTPM_NO_* macro tests - name: fwtpm-no-policy + os: ubuntu-latest wolftpm_config: --enable-fwtpm --enable-swtpm wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen build_only: true extra_cflags: -DFWTPM_NO_POLICY - name: fwtpm-no-nv + os: ubuntu-latest wolftpm_config: --enable-fwtpm --enable-swtpm wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen build_only: true extra_cflags: -DFWTPM_NO_NV - name: fwtpm-no-attestation + os: ubuntu-latest wolftpm_config: --enable-fwtpm --enable-swtpm wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen build_only: true extra_cflags: -DFWTPM_NO_ATTESTATION - name: fwtpm-no-credential + os: ubuntu-latest wolftpm_config: --enable-fwtpm --enable-swtpm wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen build_only: true extra_cflags: -DFWTPM_NO_CREDENTIAL - name: fwtpm-no-da + os: ubuntu-latest wolftpm_config: --enable-fwtpm --enable-swtpm wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen build_only: true extra_cflags: -DFWTPM_NO_DA - name: fwtpm-no-param-enc + os: ubuntu-latest wolftpm_config: --enable-fwtpm --enable-swtpm wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen build_only: true @@ -106,12 +120,14 @@ jobs: # Build-only: cross-algorithm + feature macro combinations - name: fwtpm-no-rsa-no-policy + os: ubuntu-latest wolftpm_config: --enable-fwtpm --enable-swtpm wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen --disable-rsa build_only: true extra_cflags: -DFWTPM_NO_POLICY - name: fwtpm-no-ecc-no-nv + os: ubuntu-latest wolftpm_config: --enable-fwtpm --enable-swtpm wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen --disable-ecc build_only: true @@ -119,6 +135,7 @@ jobs: # Build-only: WOLFTPM_SMALL_STACK (heap-allocated crypto objects) - name: fwtpm-small-stack + os: ubuntu-latest wolftpm_config: --enable-fwtpm --enable-swtpm wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen build_only: true @@ -126,6 +143,7 @@ jobs: # Build-only: pedantic warnings with -Werror (GCC) - name: fwtpm-pedantic-gcc + os: ubuntu-latest wolftpm_config: --enable-fwtpm --enable-swtpm wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen build_only: true @@ -133,6 +151,7 @@ jobs: # Build-only: pedantic warnings with -Werror (clang) - name: fwtpm-pedantic-clang + os: ubuntu-latest wolftpm_config: --enable-fwtpm --enable-swtpm wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen build_only: true @@ -141,6 +160,7 @@ jobs: # Build-only: pedantic fwTPM-only (no client library) - name: fwtpm-pedantic-only + os: ubuntu-latest wolftpm_config: --enable-fwtpm-only wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen build_only: true @@ -148,6 +168,13 @@ jobs: # Note: ASan / UBSan / LeakSan coverage moved to sanitizer.yml + # macOS: fwTPM with socket transport + - name: fwtpm-macos-socket + wolftpm_config: --enable-fwtpm --enable-swtpm --enable-debug + wolfssl_config: --enable-wolftpm --enable-pkcallbacks --enable-keygen + build_only: false + os: macos-latest + steps: - name: Checkout wolfTPM uses: actions/checkout@v4 @@ -159,11 +186,23 @@ jobs: path: wolfssl - name: Install tpm2-tools - if: ${{ !matrix.build_only }} + if: ${{ !matrix.build_only && runner.os != 'macOS' }} run: | sudo apt-get update sudo apt-get install -y tpm2-tools libtss2-tcti-mssim0 + - name: macOS build deps (skip tpm2-tools) + if: ${{ !matrix.build_only && runner.os == 'macOS' }} + run: | + # wolfSSL's autogen.sh needs autoreconf (autoconf+automake+libtool), + # which aren't on the macos-latest runner image by default. + # tpm2-tss / tpm2-tools are no longer in homebrew-core and their + # source build hits multiple Linux-isms (prctl.h, mremap, endian.h, + # uchar.h, -Wl,-z,relro). fwtpm_check.sh treats tpm2-tools as + # optional and will SKIP the tpm2_tools_test.sh stage when absent. + brew install autoconf automake libtool pkg-config + echo "tpm2-tools install skipped on macOS; fwtpm_check.sh handles absence" + - name: Build wolfSSL working-directory: ./wolfssl run: | @@ -182,7 +221,9 @@ jobs: CC=${{ matrix.cc || 'gcc' }} eval ./configure $CONFIGURE_ARGS make sudo make install - sudo ldconfig + if command -v ldconfig >/dev/null 2>&1; then + sudo ldconfig + fi - name: Build wolfTPM run: | @@ -208,7 +249,17 @@ jobs: if: ${{ !matrix.build_only }} env: WOLFSSL_PATH: ./wolfssl - run: make check + run: | + if command -v unshare >/dev/null 2>&1; then + FWTPM_USE_FIXED_PORT=1 \ + sudo -E unshare --net /bin/bash -c ' + set -e + ip link set lo up + make check + ' + else + make check + fi - name: Print test-suite.log on failure if: ${{ failure() && !matrix.build_only }} @@ -404,27 +455,23 @@ jobs: CFLAGS="${{ matrix.extra_cflags }} -g -O1" make - - name: Start fwtpm_server - run: | - rm -f fwtpm_nv.bin - ./src/fwtpm/fwtpm_server > /tmp/fwtpm_server.log 2>&1 & - echo $! > /tmp/fwtpm_server.pid - sleep 1 - kill -0 $(cat /tmp/fwtpm_server.pid) - - name: Run unit.test under valgrind run: | - valgrind --error-exitcode=1 --leak-check=full \ - --errors-for-leak-kinds=definite \ - --show-leak-kinds=definite \ - ./tests/unit.test - - - name: Stop fwtpm_server - if: always() - run: | - if [ -f /tmp/fwtpm_server.pid ]; then - kill $(cat /tmp/fwtpm_server.pid) 2>/dev/null || true - fi + sudo -E unshare --net /bin/bash -c ' + ip link set lo up + rm -f fwtpm_nv.bin + ./src/fwtpm/fwtpm_server > /tmp/fwtpm_server.log 2>&1 & + SERVER_PID=$! + sleep 1 + kill -0 $SERVER_PID + valgrind --error-exitcode=1 --leak-check=full \ + --errors-for-leak-kinds=definite \ + --show-leak-kinds=definite \ + ./tests/unit.test + RC=$? + kill $SERVER_PID 2>/dev/null || true + exit $RC + ' - name: Upload failure logs if: failure() diff --git a/.github/workflows/sanitizer.yml b/.github/workflows/sanitizer.yml index a1bf1c21..6d70e80c 100644 --- a/.github/workflows/sanitizer.yml +++ b/.github/workflows/sanitizer.yml @@ -78,7 +78,13 @@ jobs: LD_LIBRARY_PATH: /tmp/wolfssl-install/lib ASAN_OPTIONS: ${{ matrix.asan_options }} UBSAN_OPTIONS: ${{ matrix.ubsan_options }} - run: make check + run: | + FWTPM_USE_FIXED_PORT=1 \ + sudo -E unshare --net /bin/bash -c ' + set -e + ip link set lo up + make check + ' - name: Upload failure logs if: failure() diff --git a/CMakeLists.txt b/CMakeLists.txt index 29ba4bac..3f02a790 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -202,6 +202,11 @@ if("${WOLFTPM_INTERFACE}" STREQUAL "SWTPM") "Set SWTPM socket port (default: 2321)") list(APPEND WOLFTPM_DEFINITIONS "-DTPM2_SWTPM_PORT=${WOLFTPM_SWTPM_PORT}") + # Link Winsock for socket transport on Windows + if(WIN32 AND BUILD_WOLFTPM_LIB) + target_link_libraries(wolftpm PRIVATE ws2_32) + endif() + elseif("${WOLFTPM_INTERFACE}" STREQUAL "DEVTPM") list(APPEND WOLFTPM_DEFINITIONS "-DWOLFTPM_LINUX_DEV") @@ -475,6 +480,9 @@ function(add_tpm_example name src) examples/${src} ) target_link_libraries(${name} PRIVATE wolftpm tpm_test_lib wolftpm_wolfssl_dep) + if(WIN32) + target_link_libraries(${name} PRIVATE ws2_32) + endif() endfunction() #################################################### @@ -520,8 +528,13 @@ if(WOLFTPM_FWTPM) ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ) - if(UNIX AND NOT APPLE) - target_link_libraries(fwtpm_server PRIVATE pthread rt) + if(UNIX) + target_link_libraries(fwtpm_server PRIVATE pthread) + if(NOT APPLE) + target_link_libraries(fwtpm_server PRIVATE rt) + endif() + elseif(WIN32) + target_link_libraries(fwtpm_server PRIVATE ws2_32) endif() # fwtpm_unit_test executable @@ -538,7 +551,7 @@ if(WOLFTPM_FWTPM) ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ) - if(UNIX AND NOT APPLE) + if(UNIX) target_link_libraries(fwtpm_unit_test PRIVATE pthread) endif() diff --git a/configure.ac b/configure.ac index 0ecf67d8..4128f1c1 100644 --- a/configure.ac +++ b/configure.ac @@ -263,7 +263,7 @@ if test "x$WOLFTPM_HW_SELECTED" = "xno"; then case $host_cpu in x86_64|amd64|aarch64) case $host_os in - *mingw*|*cygwin*|*msys*|*darwin*|*win32*) + *mingw*|*cygwin*|*msys*|*win32*) ;; *) WOLFTPM_DEFAULT_FWTPM=yes @@ -370,7 +370,13 @@ then # as a compile flag only for the fwtpm_server target in src/fwtpm/include.am. if test "x$ENABLED_SWTPM" != "xyes" && test "x$ENABLED_SWTPM" != "xuart" then - # TIS/shared-memory transport for fwTPM (instead of socket) + # TIS/shared-memory transport uses POSIX mmap/sem_open — not available + # on Windows. Require socket transport (--enable-swtpm) on Windows. + case $host_os in + *mingw*|*cygwin*|*msys*|*win32*) + AC_MSG_ERROR([fwTPM TIS/SHM transport is not supported on Windows. Use --enable-fwtpm --enable-swtpm for socket transport.]) + ;; + esac AM_CFLAGS="$AM_CFLAGS -DWOLFTPM_FWTPM_HAL -DWOLFTPM_ADV_IO" ENABLED_FWTPM_TIS=yes fi diff --git a/scripts/tpm2_tools_test.sh b/scripts/tpm2_tools_test.sh index 691c2e74..d255e8ce 100755 --- a/scripts/tpm2_tools_test.sh +++ b/scripts/tpm2_tools_test.sh @@ -1195,13 +1195,21 @@ if check_tool tpm2_certifycreation; then -f plain -s rsassa # Negative: tamper with the creation ticket and confirm the TPM rejects - # it with TPM_RC_TICKET (HMAC verification fails). Flip a byte in the - # middle of the ticket so headers/sizes stay valid. + # it with TPM_RC_TICKET (HMAC verification fails). tpm2-tools may save + # the ticket with a leading marshaling header, so tampering a single + # byte at a fixed offset isn't reliable across tool versions. Overwrite + # the trailing half of the file with 0xAA — the HMAC digest lives at + # the end of the TPMT_TK_CREATION structure and is always included in + # verification, so corrupting it guarantees TPM_RC_TICKET regardless + # of any wrapper framing. cp "$TEST_TMPDIR/cc_creation.ticket" "$TEST_TMPDIR/cc_creation.ticket.bad" - # The TPMT_TK_CREATION layout starts with tag(2)+hierarchy(4); tamper - # at offset 16 which lands inside the digest body. - printf '\xAA' | dd of="$TEST_TMPDIR/cc_creation.ticket.bad" \ - bs=1 count=1 seek=16 conv=notrunc 2>/dev/null + TICKET_SIZE=$(wc -c < "$TEST_TMPDIR/cc_creation.ticket.bad") + TAMPER_OFFSET=$((TICKET_SIZE / 2)) + TAMPER_LEN=$((TICKET_SIZE - TAMPER_OFFSET)) + dd if=/dev/zero bs=1 count="$TAMPER_LEN" 2>/dev/null \ + | tr '\000' '\252' \ + | dd of="$TEST_TMPDIR/cc_creation.ticket.bad" \ + bs=1 seek="$TAMPER_OFFSET" conv=notrunc 2>/dev/null run_test_fail "certifycreation rejects tampered ticket (TPM_RC_TICKET)" \ tpm2_certifycreation -C "$TEST_TMPDIR/cc_sign.ctx" \ -c "$TEST_TMPDIR/cc_primary.ctx" \ diff --git a/src/fwtpm/fwtpm_command.c b/src/fwtpm/fwtpm_command.c index 3d2ef942..6a05375e 100644 --- a/src/fwtpm/fwtpm_command.c +++ b/src/fwtpm/fwtpm_command.c @@ -981,7 +981,7 @@ static TPM_RC FwCmd_GetCapability(FWTPM_CTX* ctx, TPM2_Packet* cmd, } case TPM_CAP_TPM_PROPERTIES: { - static const struct { + const struct { UINT32 prop; UINT32 val; } allProps[] = { @@ -4427,6 +4427,9 @@ static TPM_RC FwCmd_Import(FWTPM_CTX* ctx, TPM2_Packet* cmd, if (symKeySz <= 0) { symKeySz = 16; } + if (symKeySz > FWTPM_MAX_SYM_KEY_SIZE) { + rc = TPM_RC_SYMMETRIC; + } digestSz = TPM2_GetHashDigestSize(parentNameAlg); wcHash = FwGetWcHashType(parentNameAlg); if (digestSz <= 0 || wcHash == WC_HASH_TYPE_NONE) { @@ -4768,6 +4771,9 @@ static TPM_RC FwCmd_Duplicate(FWTPM_CTX* ctx, TPM2_Packet* cmd, if (symKeySz <= 0) { symKeySz = 16; /* default AES-128 */ } + if (symKeySz > FWTPM_MAX_SYM_KEY_SIZE) { + rc = TPM_RC_SYMMETRIC; + } } /* Ensure object name is computed */ @@ -5188,6 +5194,9 @@ static TPM_RC FwCmd_Rewrap(FWTPM_CTX* ctx, TPM2_Packet* cmd, symKeySz = (int)(oldParent->pub.parameters.eccDetail.symmetric.keyBits.sym / 8); } if (symKeySz <= 0) symKeySz = 16; + if (symKeySz > FWTPM_MAX_SYM_KEY_SIZE) { + rc = TPM_RC_SYMMETRIC; + } digestSz = TPM2_GetHashDigestSize(parentNameAlg); if (digestSz <= 0) { rc = TPM_RC_HASH; @@ -5247,6 +5256,9 @@ static TPM_RC FwCmd_Rewrap(FWTPM_CTX* ctx, TPM2_Packet* cmd, symKeySz = (int)(newParent->pub.parameters.eccDetail.symmetric.keyBits.sym / 8); } if (symKeySz <= 0) symKeySz = 16; + if (symKeySz > FWTPM_MAX_SYM_KEY_SIZE) { + rc = TPM_RC_SYMMETRIC; + } digestSz = TPM2_GetHashDigestSize(parentNameAlg); if (digestSz <= 0) rc = TPM_RC_HASH; diff --git a/src/fwtpm/fwtpm_io.c b/src/fwtpm/fwtpm_io.c index 90fe6a64..95b15423 100644 --- a/src/fwtpm/fwtpm_io.c +++ b/src/fwtpm/fwtpm_io.c @@ -80,18 +80,30 @@ int FWTPM_IO_IsStopRequested(void) #ifndef WOLFTPM_FWTPM_TIS /* --- Low-level socket helpers --- */ -static int SocketSend(int fd, const void* buf, int sz) +static int SocketSend(SOCKET_T fd, const void* buf, int sz) { const char* ptr = (const char*)buf; int remaining = sz; while (remaining > 0) { + #ifdef _WIN32 + int sent = send(fd, ptr, remaining, 0); + #else int sent = (int)write(fd, ptr, remaining); + #endif if (sent <= 0) { - if (errno == EINTR) { - continue; - } + #ifdef _WIN32 + if (WSAGetLastError() == WSAEINTR) continue; + #else + if (errno == EINTR) continue; + #endif #ifdef DEBUG_WOLFTPM - printf("fwTPM: send error %d (%s)\n", errno, strerror(errno)); + printf("fwTPM: send error %d\n", + #ifdef _WIN32 + WSAGetLastError() + #else + errno + #endif + ); #endif return TPM_RC_FAILURE; } @@ -101,22 +113,34 @@ static int SocketSend(int fd, const void* buf, int sz) return TPM_RC_SUCCESS; } -static int SocketRecv(int fd, void* buf, int sz) +static int SocketRecv(SOCKET_T fd, void* buf, int sz) { char* ptr = (char*)buf; int remaining = sz; while (remaining > 0) { + #ifdef _WIN32 + int got = recv(fd, ptr, remaining, 0); + #else int got = (int)read(fd, ptr, remaining); + #endif if (got <= 0) { - if (got < 0 && errno == EINTR) { - continue; - } + #ifdef _WIN32 + if (got < 0 && WSAGetLastError() == WSAEINTR) continue; + #else + if (got < 0 && errno == EINTR) continue; + #endif #ifdef DEBUG_WOLFTPM if (got == 0) { printf("fwTPM: recv EOF\n"); } else { - printf("fwTPM: recv error %d (%s)\n", errno, strerror(errno)); + printf("fwTPM: recv error %d\n", + #ifdef _WIN32 + WSAGetLastError() + #else + errno + #endif + ); } #endif return TPM_RC_FAILURE; @@ -127,43 +151,42 @@ static int SocketRecv(int fd, void* buf, int sz) return TPM_RC_SUCCESS; } -static int CreateListenSocket(int port) +static SOCKET_T CreateListenSocket(int port) { - int fd; + SOCKET_T fd; int optval = 1; struct sockaddr_in addr; fd = socket(AF_INET, SOCK_STREAM, 0); - if (fd < 0) { + if (fd == FWTPM_INVALID_FD) { #ifdef DEBUG_WOLFTPM - printf("fwTPM: socket() failed: %d (%s)\n", errno, strerror(errno)); + printf("fwTPM: socket() failed\n"); #endif - return -1; + return FWTPM_INVALID_FD; } - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, + (const char*)&optval, sizeof(optval)); XMEMSET(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); addr.sin_port = htons((unsigned short)port); - if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) { + if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) != 0) { #ifdef DEBUG_WOLFTPM - printf("fwTPM: bind(%d) failed: %d (%s)\n", port, errno, - strerror(errno)); + printf("fwTPM: bind(%d) failed\n", port); #endif - close(fd); - return -1; + CloseSocket(fd); + return FWTPM_INVALID_FD; } - if (listen(fd, 1) < 0) { + if (listen(fd, 1) != 0) { #ifdef DEBUG_WOLFTPM - printf("fwTPM: listen(%d) failed: %d (%s)\n", port, errno, - strerror(errno)); + printf("fwTPM: listen(%d) failed\n", port); #endif - close(fd); - return -1; + CloseSocket(fd); + return FWTPM_INVALID_FD; } return fd; @@ -476,27 +499,42 @@ int FWTPM_IO_Init(FWTPM_CTX* ctx) #ifdef WOLFTPM_FWTPM_TIS return FWTPM_TIS_Init(ctx); #else +#ifdef _WIN32 + { + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { + fprintf(stderr, "fwTPM: WSAStartup failed\n"); + return TPM_RC_FAILURE; + } + } +#endif XMEMSET(&ctx->io, 0, sizeof(ctx->io)); - ctx->io.listenFd = -1; - ctx->io.platListenFd = -1; - ctx->io.clientFd = -1; - ctx->io.platClientFd = -1; + ctx->io.listenFd = FWTPM_INVALID_FD; + ctx->io.platListenFd = FWTPM_INVALID_FD; + ctx->io.clientFd = FWTPM_INVALID_FD; + ctx->io.platClientFd = FWTPM_INVALID_FD; /* Create command port listener */ ctx->io.listenFd = CreateListenSocket(ctx->cmdPort); - if (ctx->io.listenFd < 0) { + if (ctx->io.listenFd == FWTPM_INVALID_FD) { fprintf(stderr, "fwTPM: Failed to listen on command port %d\n", ctx->cmdPort); + #ifdef _WIN32 + WSACleanup(); + #endif return TPM_RC_FAILURE; } /* Create platform port listener */ ctx->io.platListenFd = CreateListenSocket(ctx->platPort); - if (ctx->io.platListenFd < 0) { + if (ctx->io.platListenFd == FWTPM_INVALID_FD) { fprintf(stderr, "fwTPM: Failed to listen on platform port %d\n", ctx->platPort); - close(ctx->io.listenFd); - ctx->io.listenFd = -1; + CloseSocket(ctx->io.listenFd); + ctx->io.listenFd = FWTPM_INVALID_FD; + #ifdef _WIN32 + WSACleanup(); + #endif return TPM_RC_FAILURE; } @@ -516,22 +554,25 @@ void FWTPM_IO_Cleanup(FWTPM_CTX* ctx) #ifdef WOLFTPM_FWTPM_TIS FWTPM_TIS_Cleanup(ctx); #else - if (ctx->io.clientFd >= 0) { - close(ctx->io.clientFd); - ctx->io.clientFd = -1; + if (ctx->io.clientFd != FWTPM_INVALID_FD) { + CloseSocket(ctx->io.clientFd); + ctx->io.clientFd = FWTPM_INVALID_FD; } - if (ctx->io.platClientFd >= 0) { - close(ctx->io.platClientFd); - ctx->io.platClientFd = -1; + if (ctx->io.platClientFd != FWTPM_INVALID_FD) { + CloseSocket(ctx->io.platClientFd); + ctx->io.platClientFd = FWTPM_INVALID_FD; } - if (ctx->io.listenFd >= 0) { - close(ctx->io.listenFd); - ctx->io.listenFd = -1; + if (ctx->io.listenFd != FWTPM_INVALID_FD) { + CloseSocket(ctx->io.listenFd); + ctx->io.listenFd = FWTPM_INVALID_FD; } - if (ctx->io.platListenFd >= 0) { - close(ctx->io.platListenFd); - ctx->io.platListenFd = -1; + if (ctx->io.platListenFd != FWTPM_INVALID_FD) { + CloseSocket(ctx->io.platListenFd); + ctx->io.platListenFd = FWTPM_INVALID_FD; } +#ifdef _WIN32 + WSACleanup(); +#endif #endif /* !WOLFTPM_FWTPM_TIS */ } @@ -541,8 +582,8 @@ int FWTPM_IO_ServerLoop(FWTPM_CTX* ctx) int rc = TPM_RC_SUCCESS; fd_set readFds; int maxFd; - int cmdFd = -1; /* active command client fd */ - int platFd = -1; /* active platform client fd */ + SOCKET_T cmdFd = FWTPM_INVALID_FD; /* active command client fd */ + SOCKET_T platFd = FWTPM_INVALID_FD; /* active platform client fd */ struct timeval tv; int selRc; #ifndef _WIN32 @@ -588,11 +629,11 @@ int FWTPM_IO_ServerLoop(FWTPM_CTX* ctx) maxFd = ctx->io.platListenFd; /* Watch active client connections for incoming data */ - if (cmdFd >= 0) { + if (cmdFd != FWTPM_INVALID_FD) { FD_SET(cmdFd, &readFds); if (cmdFd > maxFd) maxFd = cmdFd; } - if (platFd >= 0) { + if (platFd != FWTPM_INVALID_FD) { FD_SET(platFd, &readFds); if (platFd > maxFd) maxFd = platFd; } @@ -601,11 +642,13 @@ int FWTPM_IO_ServerLoop(FWTPM_CTX* ctx) tv.tv_usec = 0; selRc = select(maxFd + 1, &readFds, NULL, NULL, &tv); if (selRc < 0) { - if (errno == EINTR) { - continue; - } + #ifdef _WIN32 + if (WSAGetLastError() == WSAEINTR) continue; + #else + if (errno == EINTR) continue; + #endif #ifdef DEBUG_WOLFTPM - printf("fwTPM: select error %d (%s)\n", errno, strerror(errno)); + printf("fwTPM: select error\n"); #endif rc = TPM_RC_FAILURE; break; @@ -616,13 +659,13 @@ int FWTPM_IO_ServerLoop(FWTPM_CTX* ctx) /* Accept new platform connection */ if (FD_ISSET(ctx->io.platListenFd, &readFds)) { - int newFd = accept(ctx->io.platListenFd, NULL, NULL); - if (newFd >= 0) { - if (platFd >= 0) { + SOCKET_T newFd = accept(ctx->io.platListenFd, NULL, NULL); + if (newFd != FWTPM_INVALID_FD) { + if (platFd != FWTPM_INVALID_FD) { #ifdef DEBUG_WOLFTPM printf("fwTPM: platform connection replaced\n"); #endif - close(platFd); + CloseSocket(platFd); } platFd = newFd; } @@ -630,37 +673,37 @@ int FWTPM_IO_ServerLoop(FWTPM_CTX* ctx) /* Accept new command connection */ if (FD_ISSET(ctx->io.listenFd, &readFds)) { - int newFd = accept(ctx->io.listenFd, NULL, NULL); - if (newFd >= 0) { - if (cmdFd >= 0) { + SOCKET_T newFd = accept(ctx->io.listenFd, NULL, NULL); + if (newFd != FWTPM_INVALID_FD) { + if (cmdFd != FWTPM_INVALID_FD) { #ifdef DEBUG_WOLFTPM printf("fwTPM: command connection replaced\n"); #endif - close(cmdFd); + CloseSocket(cmdFd); } cmdFd = newFd; } } /* Handle one message from active platform client */ - if (platFd >= 0 && FD_ISSET(platFd, &readFds)) { + if (platFd != FWTPM_INVALID_FD && FD_ISSET(platFd, &readFds)) { if (HandlePlatformCommand(ctx, platFd) != TPM_RC_SUCCESS) { - close(platFd); - platFd = -1; + CloseSocket(platFd); + platFd = FWTPM_INVALID_FD; } } /* Handle one message from active command client */ - if (cmdFd >= 0 && FD_ISSET(cmdFd, &readFds)) { + if (cmdFd != FWTPM_INVALID_FD && FD_ISSET(cmdFd, &readFds)) { if (HandleCommandConnection(ctx, cmdFd) != TPM_RC_SUCCESS) { - close(cmdFd); - cmdFd = -1; + CloseSocket(cmdFd); + cmdFd = FWTPM_INVALID_FD; } } } - if (cmdFd >= 0) close(cmdFd); - if (platFd >= 0) close(platFd); + if (cmdFd != FWTPM_INVALID_FD) CloseSocket(cmdFd); + if (platFd != FWTPM_INVALID_FD) CloseSocket(platFd); return rc; #endif /* !WOLFTPM_FWTPM_TIS */ diff --git a/src/fwtpm/fwtpm_main.c b/src/fwtpm/fwtpm_main.c index e38457d9..298f55c4 100644 --- a/src/fwtpm/fwtpm_main.c +++ b/src/fwtpm/fwtpm_main.c @@ -69,7 +69,9 @@ int main(int argc, char* argv[]) static FWTPM_CTX ctx; int i; int clearNv = 0; +#ifndef _WIN32 struct sigaction sa; +#endif /* Zero context before init (required so HAL save/restore works) */ XMEMSET(&ctx, 0, sizeof(ctx)); @@ -155,11 +157,16 @@ int main(int argc, char* argv[]) printf(" Model: %s\n", FWTPM_MODEL); /* Install signal handler for graceful shutdown with NV save */ +#ifdef _WIN32 + signal(SIGTERM, sigterm_handler); + signal(SIGINT, sigterm_handler); +#else sa.sa_handler = sigterm_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGTERM, &sa, NULL); sigaction(SIGINT, &sa, NULL); +#endif /* Initialize socket transport */ rc = FWTPM_IO_Init(&ctx); diff --git a/src/tpm2_swtpm.c b/src/tpm2_swtpm.c index e41580d6..34126863 100644 --- a/src/tpm2_swtpm.c +++ b/src/tpm2_swtpm.c @@ -52,7 +52,7 @@ #include #include #include -#ifndef WOLFTPM_ZEPHYR +#if !defined(WOLFTPM_ZEPHYR) && !defined(_WIN32) #include #include #include diff --git a/tests/fwtpm_check.sh b/tests/fwtpm_check.sh index ef4e6d72..40df31c1 100755 --- a/tests/fwtpm_check.sh +++ b/tests/fwtpm_check.sh @@ -30,12 +30,13 @@ SKIP_EXAMPLES=0 # Wait for a TCP port to be listening # Uses ss/netstat to check without connecting (nc -z would consume the accept slot) +# Port separator in netstat output is ':' on Linux and '.' on macOS. wait_for_port() { local port="$1" timeout="${2:-500}" elapsed=0 while [ $elapsed -lt $timeout ]; do if command -v ss >/dev/null 2>&1; then - ss -tln 2>/dev/null | grep -q ":${port} " && return 0 - elif netstat -tln 2>/dev/null | grep -q ":${port} "; then + ss -tln 2>/dev/null | grep -qE "[:.]${port} " && return 0 + elif netstat -an 2>/dev/null | grep -qE "[:.]${port} .*LISTEN"; then return 0 fi sleep 0.01 @@ -52,10 +53,10 @@ check_port_in_use() { nc -z localhost "$port" 2>/dev/null return $? elif command -v ss >/dev/null 2>&1; then - ss -tln 2>/dev/null | grep -q ":${port} " + ss -tln 2>/dev/null | grep -qE "[:.]${port} " return $? elif command -v netstat >/dev/null 2>&1; then - netstat -tln 2>/dev/null | grep -q ":${port} " + netstat -an 2>/dev/null | grep -qE "[:.]${port} .*LISTEN" return $? fi return 2 # no probe tool — cannot determine @@ -299,10 +300,14 @@ if [ $IS_FWTPM_MODE -eq 1 ]; then done if [ $HAS_GETENV -eq 1 ] && [ $IS_SWTPM_MODE -eq 1 ]; then - FWTPM_PORT=$(pick_available_port) - if [ -z "$FWTPM_PORT" ]; then - echo "FAIL: Could not find available port" - exit 1 + if [ "${FWTPM_USE_FIXED_PORT:-0}" != "1" ]; then + FWTPM_PORT=$(pick_available_port) + if [ -z "$FWTPM_PORT" ]; then + echo "FAIL: Could not find available port" + exit 1 + fi + else + echo "Using fixed port $FWTPM_PORT (namespace isolation)" fi FWTPM_PLAT_PORT=$((FWTPM_PORT + 1)) export TPM2_SWTPM_PORT="$FWTPM_PORT" diff --git a/wolftpm/fwtpm/fwtpm.h b/wolftpm/fwtpm/fwtpm.h index 3f012344..8b90288a 100644 --- a/wolftpm/fwtpm/fwtpm.h +++ b/wolftpm/fwtpm/fwtpm.h @@ -424,11 +424,16 @@ typedef struct FWTPM_IO_HAL_S { } FWTPM_IO_HAL; /* IO context for socket transport (default) */ +#ifdef _WIN32 + #define FWTPM_INVALID_FD INVALID_SOCKET +#else + #define FWTPM_INVALID_FD (-1) +#endif typedef struct FWTPM_IO_CTX { - int listenFd; /* Listening socket for command port */ - int platListenFd; /* Listening socket for platform port */ - int clientFd; /* Accepted client connection */ - int platClientFd; /* Accepted platform client connection */ + SOCKET_T listenFd; /* Listening socket for command port */ + SOCKET_T platListenFd; /* Listening socket for platform port */ + SOCKET_T clientFd; /* Accepted client connection */ + SOCKET_T platClientFd; /* Accepted platform client connection */ } FWTPM_IO_CTX; #endif /* !WOLFTPM_FWTPM_TIS */