/* $Id: tpm-win.cpp $ */ /** @file * IPRT - Trusted Platform Module (TPM) access, Windows variant. */ /* * Copyright (C) 2021-2023 Oracle and/or its affiliates. * * This file is part of VirtualBox base platform packages, as * available from https://www.virtualbox.org. * * This program 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, in version 3 of the * License. * * This program 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, see . * * The contents of this file may alternatively be used under the terms * of the Common Development and Distribution License Version 1.0 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included * in the VirtualBox distribution, in which case the provisions of the * CDDL are applicable instead of those of the GPL. * * You may elect to license modified versions of this file under the * terms and conditions of either the GPL or the CDDL or both. * * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 */ /********************************************************************************************************************************* * Header Files * *********************************************************************************************************************************/ #define LOG_GROUP RTLOGGROUP_DEFAULT #include #include #include #include #include #include #include #include "internal-r3-win.h" #include /********************************************************************************************************************************* * Defined Constants And Macros * *********************************************************************************************************************************/ /* tbs.dll: */ typedef struct TBS_CONTEXT_PARAMS2 { UINT32 version; union { struct { UINT32 requestRaw: 1; UINT32 includeTpm12: 1; UINT32 includeTpm20: 1; } Fields; UINT32 asUINT32; } u; } TBS_CONTEXT_PARAMS2; typedef struct TBS_DEVICE_INFO { UINT32 structVersion; UINT32 tpmVersion; UINT32 tpmInterfaceType; UINT32 tpmImpRevision; } TBS_DEVICE_INFO; #define TPM_VERSION_12 1 #define TPM_VERSION_20 2 #define TBS_SUCCESS S_OK #define TBS_COMMAND_PRIORITY_NORMAL 200 typedef UINT32 TBS_RESULT; typedef void *TBS_HCONTEXT; typedef TBS_RESULT (WINAPI *PFNTBSI_CONTEXT_CREATE)(const TBS_CONTEXT_PARAMS2 *, TBS_HCONTEXT *); typedef TBS_RESULT (WINAPI *PFNTBSI_CONTEXT_CLOSE)(TBS_HCONTEXT); typedef TBS_RESULT (WINAPI *PFNTBSI_GET_DEVICE_INFO)(UINT32, TBS_DEVICE_INFO *); typedef TBS_RESULT (WINAPI *PFNTBSI_CANCEL_COMMANDS)(TBS_HCONTEXT); typedef TBS_RESULT (WINAPI *PFNTBSI_SUBMIT_COMMANDS)(TBS_HCONTEXT, UINT32, UINT32, const BYTE *, UINT32, PBYTE, PUINT32); /********************************************************************************************************************************* * Structures and Typedefs * *********************************************************************************************************************************/ /** * Internal TPM instance data. */ typedef struct RTTPMINT { /** Handle to the TPM context. */ TBS_HCONTEXT hCtx; /** The deduced TPM version. */ RTTPMVERSION enmTpmVers; } RTTPMINT; /** Pointer to the internal TPM instance data. */ typedef RTTPMINT *PRTTPMINT; /********************************************************************************************************************************* * Global Variables * *********************************************************************************************************************************/ /** Init once structure. */ static RTONCE g_rtTpmWinInitOnce = RTONCE_INITIALIZER; /* tbs.dll: */ static PFNTBSI_CONTEXT_CREATE g_pfnTbsiContextCreate = NULL; static PFNTBSI_CONTEXT_CLOSE g_pfnTbsiContextClose = NULL; static PFNTBSI_GET_DEVICE_INFO g_pfnTbsiGetDeviceInfo = NULL; static PFNTBSI_CANCEL_COMMANDS g_pfnTbsiCancelCommands = NULL; static PFNTBSI_SUBMIT_COMMANDS g_pfnTbsiSubmitCommands = NULL; /********************************************************************************************************************************* * Internal Functions * *********************************************************************************************************************************/ /** * Initialize the globals. * * @returns IPRT status code. * @param pvUser Ignored. */ static DECLCALLBACK(int32_t) rtTpmWinInitOnce(void *pvUser) { RT_NOREF(pvUser); RTLDRMOD hMod; int rc = RTLdrLoadSystem("tbs.dll", true /*fNoUnload*/, &hMod); if (RT_SUCCESS(rc)) { rc = RTLdrGetSymbol(hMod, "Tbsi_Context_Create", (void **)&g_pfnTbsiContextCreate); if (RT_FAILURE(rc)) return rc; rc = RTLdrGetSymbol(hMod, "Tbsip_Context_Close", (void **)&g_pfnTbsiContextClose); if (RT_FAILURE(rc)) return rc; rc = RTLdrGetSymbol(hMod, "Tbsip_Cancel_Commands", (void **)&g_pfnTbsiCancelCommands); if (RT_FAILURE(rc)) return rc; rc = RTLdrGetSymbol(hMod, "Tbsip_Submit_Command", (void **)&g_pfnTbsiSubmitCommands); if (RT_FAILURE(rc)) return rc; rc = RTLdrGetSymbol(hMod, "Tbsi_GetDeviceInfo", (void **)&g_pfnTbsiGetDeviceInfo); if (RT_FAILURE(rc)) { g_pfnTbsiGetDeviceInfo = NULL; Assert(g_enmWinVer < kRTWinOSType_8); } RTLdrClose(hMod); } return rc; } RTDECL(int) RTTpmOpen(PRTTPM phTpm, uint32_t idTpm) { AssertPtrReturn(phTpm, VERR_INVALID_POINTER); if (idTpm == RTTPM_ID_DEFAULT) idTpm = 0; AssertReturn(idTpm == 0, VERR_NOT_SUPPORTED); /* * Initialize the globals. */ int rc = RTOnce(&g_rtTpmWinInitOnce, rtTpmWinInitOnce, NULL); AssertRCReturn(rc, rc); PRTTPMINT pThis = (PRTTPMINT)RTMemAllocZ(sizeof(*pThis)); if (pThis) { TBS_CONTEXT_PARAMS2 CtxParams; RT_ZERO(CtxParams); CtxParams.version = TPM_VERSION_12; if (g_pfnTbsiGetDeviceInfo) { /* TPM2 support is available starting with Win8 which has Tbsi_GetDeviceInfo available. */ TBS_DEVICE_INFO DevInfo; RT_ZERO(DevInfo); DevInfo.structVersion = TPM_VERSION_20; TBS_RESULT rcTbs = g_pfnTbsiGetDeviceInfo(sizeof(DevInfo), &DevInfo); if (rcTbs == TBS_SUCCESS) { CtxParams.version = TPM_VERSION_20; if (DevInfo.tpmVersion == TPM_VERSION_20) { pThis->enmTpmVers = RTTPMVERSION_2_0; CtxParams.u.Fields.includeTpm20 = 1; } else { Assert(DevInfo.tpmVersion == TPM_VERSION_12); pThis->enmTpmVers = RTTPMVERSION_1_2; CtxParams.u.Fields.includeTpm12 = 1; } } else rc = VERR_NOT_FOUND; } else pThis->enmTpmVers = RTTPMVERSION_1_2; if (RT_SUCCESS(rc)) { TBS_RESULT rcTbs = g_pfnTbsiContextCreate(&CtxParams, &pThis->hCtx); if (rcTbs == TBS_SUCCESS) { *phTpm = pThis; return VINF_SUCCESS; } else rc = VERR_NOT_FOUND; } RTMemFree(pThis); } else rc = VERR_NO_MEMORY; return rc; } RTDECL(int) RTTpmClose(RTTPM hTpm) { PRTTPMINT pThis = hTpm; AssertPtrReturn(pThis, VERR_INVALID_HANDLE); TBS_RESULT rcTbs = g_pfnTbsiContextClose(pThis->hCtx); Assert(rcTbs == TBS_SUCCESS); RT_NOREF(rcTbs); RTMemFree(pThis); return VINF_SUCCESS; } RTDECL(RTTPMVERSION) RTTpmGetVersion(RTTPM hTpm) { PRTTPMINT pThis = hTpm; AssertPtrReturn(pThis, RTTPMVERSION_INVALID); return pThis->enmTpmVers; } RTDECL(uint32_t) RTTpmGetLocalityMax(RTTPM hTpm) { RT_NOREF(hTpm); return 0; /* Only TPM locality 0 is supported. */ } RTDECL(int) RTTpmReqCancel(RTTPM hTpm) { PRTTPMINT pThis = hTpm; AssertPtrReturn(pThis, VERR_INVALID_HANDLE); TBS_RESULT rcTbs = g_pfnTbsiCancelCommands(pThis->hCtx); if (rcTbs != TBS_SUCCESS) return VERR_DEV_IO_ERROR; return VINF_SUCCESS; } RTDECL(int) RTTpmReqExec(RTTPM hTpm, uint8_t bLoc, const void *pvReq, size_t cbReq, void *pvResp, size_t cbRespMax, size_t *pcbResp) { PRTTPMINT pThis = hTpm; AssertPtrReturn(pThis, VERR_INVALID_HANDLE); AssertPtrReturn(pvReq, VERR_INVALID_POINTER); AssertPtrReturn(pvResp, VERR_INVALID_POINTER); AssertReturn(cbReq && cbRespMax, VERR_INVALID_PARAMETER); AssertReturn(cbReq == (UINT32)cbReq && cbRespMax == (UINT32)cbRespMax, VERR_BUFFER_OVERFLOW); AssertReturn(bLoc == 0, VERR_NOT_SUPPORTED); /* TBS doesn't support another locality than 0. */ UINT32 cbResult = (UINT32)cbRespMax; TBS_RESULT rcTbs = g_pfnTbsiSubmitCommands(pThis->hCtx, 0 /*Locality*/, TBS_COMMAND_PRIORITY_NORMAL, (const BYTE *)pvReq, (UINT32)cbReq, (BYTE *)pvResp, &cbResult); if (rcTbs == TBS_SUCCESS) { if (pcbResp) *pcbResp = cbResult; return VINF_SUCCESS; } return VERR_DEV_IO_ERROR; }