VirtualBox

source: vbox/trunk/src/VBox/Devices/Security/DrvTpmEmuTpms.cpp

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.0 KB
Line 
1/* $Id: DrvTpmEmuTpms.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * TPM emulation driver based on libtpms.
4 */
5
6/*
7 * Copyright (C) 2021-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_DRV_TPM_EMU
33#include <VBox/vmm/pdmdrv.h>
34#include <VBox/vmm/pdmtpmifs.h>
35#include <iprt/assert.h>
36#include <iprt/file.h>
37#include <iprt/mem.h>
38#include <iprt/string.h>
39#include <iprt/semaphore.h>
40#include <iprt/uuid.h>
41#include <iprt/vfs.h>
42#include <iprt/zip.h>
43
44#include <libtpms/tpm_library.h>
45#include <libtpms/tpm_error.h>
46#include <libtpms/tpm_tis.h>
47#include <libtpms/tpm_nvfilename.h>
48
49#include <iprt/formats/tpm.h>
50
51#if 0
52#include <unistd.h>
53#endif
54#include <stdlib.h>
55
56#include "VBoxDD.h"
57
58
59/*********************************************************************************************************************************
60* Defined Constants And Macros *
61*********************************************************************************************************************************/
62
63
64/*********************************************************************************************************************************
65* Structures and Typedefs *
66*********************************************************************************************************************************/
67
68/**
69 * TPM emulation driver instance data.
70 *
71 * @implements PDMITPMCONNECTOR
72 */
73typedef struct DRVTPMEMU
74{
75 /** The stream interface. */
76 PDMITPMCONNECTOR ITpmConnector;
77 /** Pointer to the driver instance. */
78 PPDMDRVINS pDrvIns;
79 /** The VFS interface of the driver below for NVRAM/TPM state loading and storing. */
80 PPDMIVFSCONNECTOR pDrvVfs;
81
82 /** The TPM version we are emulating. */
83 TPMVERSION enmVersion;
84 /** The buffer size the TPM advertises. */
85 uint32_t cbBuffer;
86 /** Currently set locality. */
87 uint8_t bLoc;
88} DRVTPMEMU;
89/** Pointer to the TPM emulator instance data. */
90typedef DRVTPMEMU *PDRVTPMEMU;
91
92/** The special no current locality selected value. */
93#define TPM_NO_LOCALITY_SELECTED 0xff
94
95
96/*********************************************************************************************************************************
97* Global Variables *
98*********************************************************************************************************************************/
99/** Pointer to the (only) instance data in this driver. */
100static PDRVTPMEMU g_pDrvTpmEmuTpms = NULL;
101
102
103/*********************************************************************************************************************************
104* Internal Functions *
105*********************************************************************************************************************************/
106
107/* -=-=-=-=- PDMITPMCONNECTOR interface callabcks. -=-=-=-=- */
108
109
110/** @interface_method_impl{PDMITPMCONNECTOR,pfnGetVersion} */
111static DECLCALLBACK(TPMVERSION) drvTpmEmuTpmsGetVersion(PPDMITPMCONNECTOR pInterface)
112{
113 PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
114 return pThis->enmVersion;
115}
116
117
118/** @interface_method_impl{PDMITPMCONNECTOR,pfnGetLocalityMax} */
119static DECLCALLBACK(uint32_t) drvTpmEmuGetLocalityMax(PPDMITPMCONNECTOR pInterface)
120{
121 RT_NOREF(pInterface);
122 return 4;
123}
124
125
126/** @interface_method_impl{PDMITPMCONNECTOR,pfnGetBufferSize} */
127static DECLCALLBACK(uint32_t) drvTpmEmuGetBufferSize(PPDMITPMCONNECTOR pInterface)
128{
129 PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
130 return pThis->cbBuffer;
131}
132
133
134/** @interface_method_impl{PDMITPMCONNECTOR,pfnGetEstablishedFlag} */
135static DECLCALLBACK(bool) drvTpmEmuTpmsGetEstablishedFlag(PPDMITPMCONNECTOR pInterface)
136{
137 RT_NOREF(pInterface);
138
139 TPM_BOOL fTpmEst = FALSE;
140 TPM_RESULT rcTpm = TPM_IO_TpmEstablished_Get(&fTpmEst);
141 if (RT_LIKELY(rcTpm == TPM_SUCCESS))
142 return RT_BOOL(fTpmEst);
143
144 return false;
145}
146
147
148/** @interface_method_impl{PDMITPMCONNECTOR,pfnResetEstablishedFlag} */
149static DECLCALLBACK(int) drvTpmEmuTpmsResetEstablishedFlag(PPDMITPMCONNECTOR pInterface, uint8_t bLoc)
150{
151 PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
152 uint8_t bLocOld = pThis->bLoc;
153
154 pThis->bLoc = bLoc;
155 TPM_RESULT rcTpm = TPM_IO_TpmEstablished_Reset();
156 pThis->bLoc = bLocOld;
157
158 if (RT_LIKELY(rcTpm == TPM_SUCCESS))
159 return VINF_SUCCESS;
160
161 LogRelMax(10, ("DrvTpmEmuTpms#%u: Failed to reset the established flag with %#x\n",
162 pThis->pDrvIns->iInstance, rcTpm));
163 return VERR_DEV_IO_ERROR;
164}
165
166
167/** @interface_method_impl{PDMITPMCONNECTOR,pfnCmdExec} */
168static DECLCALLBACK(int) drvTpmEmuTpmsCmdExec(PPDMITPMCONNECTOR pInterface, uint8_t bLoc, const void *pvCmd, size_t cbCmd, void *pvResp, size_t cbResp)
169{
170 PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
171
172 pThis->bLoc = bLoc;
173
174 uint8_t *pbRespBuf = NULL;
175 uint32_t cbRespBuf = 0;
176 uint32_t cbRespActual = 0;
177 TPM_RESULT rcTpm = TPMLIB_Process(&pbRespBuf, &cbRespActual, &cbRespBuf, (uint8_t *)pvCmd, (uint32_t)cbCmd);
178 if (RT_LIKELY(rcTpm == TPM_SUCCESS))
179 {
180 memcpy(pvResp, pbRespBuf, RT_MIN(cbResp, cbRespActual));
181 free(pbRespBuf);
182 return VINF_SUCCESS;
183 }
184
185 LogRelMax(10, ("DrvTpmEmuTpms#%u: Failed to execute command with %#x\n",
186 pThis->pDrvIns->iInstance, rcTpm));
187 return VERR_DEV_IO_ERROR;
188}
189
190
191/** @interface_method_impl{PDMITPMCONNECTOR,pfnCmdCancel} */
192static DECLCALLBACK(int) drvTpmEmuTpmsCmdCancel(PPDMITPMCONNECTOR pInterface)
193{
194 PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
195
196 TPM_RESULT rcTpm = TPMLIB_CancelCommand();
197 if (RT_LIKELY(rcTpm == TPM_SUCCESS))
198 return VINF_SUCCESS;
199
200 LogRelMax(10, ("DrvTpmEmuTpms#%u: Failed to cancel outstanding command with %#x\n",
201 pThis->pDrvIns->iInstance, rcTpm));
202 return VERR_DEV_IO_ERROR;
203}
204
205
206/** @interface_method_impl{PDMIBASE,pfnQueryInterface} */
207static DECLCALLBACK(void *) drvTpmEmuTpmsQueryInterface(PPDMIBASE pInterface, const char *pszIID)
208{
209 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
210 PDRVTPMEMU pThis = PDMINS_2_DATA(pDrvIns, PDRVTPMEMU);
211 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
212 PDMIBASE_RETURN_INTERFACE(pszIID, PDMITPMCONNECTOR, &pThis->ITpmConnector);
213 return NULL;
214}
215
216
217/* -=-=-=-=- libtpms_callbacks -=-=-=-=- */
218
219
220static DECLCALLBACK(TPM_RESULT) drvTpmEmuTpmsCbkNvRamInit(void)
221{
222 PDRVTPMEMU pThis = g_pDrvTpmEmuTpms;
223 RT_NOREF(pThis);
224
225 return TPM_SUCCESS;
226}
227
228
229static DECLCALLBACK(TPM_RESULT) drvTpmEmuTpmsCbkNvRamLoadData(uint8_t **ppvData, uint32_t *pcbLength,
230 uint32_t idTpm, const char *pszName)
231{
232 PDRVTPMEMU pThis = g_pDrvTpmEmuTpms;
233
234 AssertReturn(idTpm == 0, TPM_FAIL);
235
236 uint64_t cbState = 0;
237 int rc = pThis->pDrvVfs->pfnQuerySize(pThis->pDrvVfs, pThis->pDrvIns->pReg->szName, pszName, &cbState);
238 if ( RT_SUCCESS(rc)
239 && cbState == (uint32_t)cbState)
240 {
241 void *pvData = malloc(cbState);
242 if (RT_LIKELY(pvData))
243 {
244 rc = pThis->pDrvVfs->pfnReadAll(pThis->pDrvVfs, pThis->pDrvIns->pReg->szName, pszName,
245 pvData, cbState);
246 if (RT_SUCCESS(rc))
247 {
248 *ppvData = (uint8_t *)pvData;
249 *pcbLength = (uint32_t)cbState;
250 return VINF_SUCCESS;
251 }
252
253 free(pvData);
254 }
255 }
256 else if (rc == VERR_NOT_FOUND)
257 return TPM_RETRY; /* This is fine for the first start of a new VM. */
258
259 return TPM_FAIL;
260}
261
262
263static DECLCALLBACK(TPM_RESULT) drvTpmEmuTpmsCbkNvRamStoreData(const uint8_t *pvData, uint32_t cbLength,
264 uint32_t idTpm, const char *pszName)
265{
266 PDRVTPMEMU pThis = g_pDrvTpmEmuTpms;
267
268 AssertReturn(idTpm == 0, TPM_FAIL);
269
270 int rc = pThis->pDrvVfs->pfnWriteAll(pThis->pDrvVfs, pThis->pDrvIns->pReg->szName, pszName,
271 pvData, cbLength);
272 if (RT_SUCCESS(rc))
273 return TPM_SUCCESS;
274
275 return TPM_FAIL;
276}
277
278
279static DECLCALLBACK(TPM_RESULT) drvTpmEmuTpmsCbkNvRamDeleteName(uint32_t idTpm, const char *pszName, TPM_BOOL fMustExist)
280{
281 PDRVTPMEMU pThis = g_pDrvTpmEmuTpms;
282
283 AssertReturn(idTpm == 0, TPM_FAIL);
284
285 int rc = pThis->pDrvVfs->pfnDelete(pThis->pDrvVfs, pThis->pDrvIns->pReg->szName, pszName);
286 if ( RT_SUCCESS(rc)
287 || ( rc == VERR_NOT_FOUND
288 && !fMustExist))
289 return TPM_SUCCESS;
290
291 return TPM_FAIL;
292}
293
294
295static DECLCALLBACK(TPM_RESULT) drvTpmEmuTpmsCbkIoInit(void)
296{
297 return TPM_SUCCESS;
298}
299
300
301static DECLCALLBACK(TPM_RESULT) drvTpmEmuTpmsCbkIoGetLocality(TPM_MODIFIER_INDICATOR *pLocalityModifier, uint32_t idTpm)
302{
303 PDRVTPMEMU pThis = g_pDrvTpmEmuTpms;
304
305 AssertReturn(idTpm == 0, TPM_FAIL);
306
307 *pLocalityModifier = pThis->bLoc;
308 return TPM_SUCCESS;
309}
310
311
312static DECLCALLBACK(TPM_RESULT) drvTpmEmuTpmsCbkIoGetPhysicalPresence(TPM_BOOL *pfPhysicalPresence, uint32_t idTpm)
313{
314 AssertReturn(idTpm == 0, TPM_FAIL);
315
316 *pfPhysicalPresence = TRUE;
317 return TPM_SUCCESS;
318}
319
320
321/* -=-=-=-=- PDMDRVREG -=-=-=-=- */
322
323/**
324 * @interface_method_impl{PDMDRVREG,pfnPowerOn}
325 */
326static DECLCALLBACK(void) drvTpmEmuTpmsPowerOn(PPDMDRVINS pDrvIns)
327{
328 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
329
330 TPM_RESULT rcTpm = TPMLIB_MainInit();
331 if (RT_UNLIKELY(rcTpm != TPM_SUCCESS))
332 {
333 LogRel(("DrvTpmEmuTpms#%u: Failed to initialize TPM emulation with %#x\n",
334 pDrvIns->iInstance, rcTpm));
335 PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS, "Failed to startup the TPM with %u", rcTpm);
336 }
337}
338
339
340/**
341 * @interface_method_impl{PDMDRVREG,pfnReset}
342 */
343static DECLCALLBACK(void) drvTpmEmuTpmsReset(PPDMDRVINS pDrvIns)
344{
345 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
346
347 TPMLIB_Terminate();
348 TPM_RESULT rcTpm = TPMLIB_MainInit();
349 if (RT_UNLIKELY(rcTpm != TPM_SUCCESS))
350 {
351 LogRel(("DrvTpmEmuTpms#%u: Failed to reset TPM emulation with %#x\n",
352 pDrvIns->iInstance, rcTpm));
353 PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS, "Failed to startup the TPM with %u", rcTpm);
354 }
355}
356
357
358/**
359 * @interface_method_impl{PDMDRVREG,pfnPowerOff}
360 */
361static DECLCALLBACK(void) drvTpmEmuTpmsPowerOff(PPDMDRVINS pDrvIns)
362{
363 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
364
365 TPMLIB_Terminate();
366}
367
368
369/** @copydoc FNPDMDRVCONSTRUCT */
370static DECLCALLBACK(int) drvTpmEmuTpmsConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
371{
372 RT_NOREF(fFlags);
373 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
374 PDRVTPMEMU pThis = PDMINS_2_DATA(pDrvIns, PDRVTPMEMU);
375 PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
376
377 /*
378 * Init the static parts.
379 */
380 pThis->pDrvIns = pDrvIns;
381 pThis->enmVersion = TPMVERSION_UNKNOWN;
382 pThis->bLoc = TPM_NO_LOCALITY_SELECTED;
383
384 /* IBase */
385 pDrvIns->IBase.pfnQueryInterface = drvTpmEmuTpmsQueryInterface;
386 /* ITpmConnector */
387 pThis->ITpmConnector.pfnGetVersion = drvTpmEmuTpmsGetVersion;
388 pThis->ITpmConnector.pfnGetLocalityMax = drvTpmEmuGetLocalityMax;
389 pThis->ITpmConnector.pfnGetBufferSize = drvTpmEmuGetBufferSize;
390 pThis->ITpmConnector.pfnGetEstablishedFlag = drvTpmEmuTpmsGetEstablishedFlag;
391 pThis->ITpmConnector.pfnResetEstablishedFlag = drvTpmEmuTpmsResetEstablishedFlag;
392 pThis->ITpmConnector.pfnCmdExec = drvTpmEmuTpmsCmdExec;
393 pThis->ITpmConnector.pfnCmdCancel = drvTpmEmuTpmsCmdCancel;
394
395 /*
396 * Validate and read the configuration.
397 */
398 PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "TpmVersion|BufferSize", "");
399
400#if 0
401 TPMLIB_SetDebugFD(STDERR_FILENO);
402 TPMLIB_SetDebugLevel(~0);
403#endif
404
405 /*
406 * Try attach the VFS driver below and query it's VFS interface.
407 */
408 PPDMIBASE pBase = NULL;
409 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBase);
410 if (RT_FAILURE(rc))
411 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
412 N_("Failed to attach driver below us! %Rrc"), rc);
413 pThis->pDrvVfs = PDMIBASE_QUERY_INTERFACE(pBase, PDMIVFSCONNECTOR);
414 if (!pThis->pDrvVfs)
415 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
416 N_("No VFS interface below"));
417
418 TPMLIB_TPMVersion enmVersion = TPMLIB_TPM_VERSION_2;
419 uint32_t uTpmVersion = 0;
420 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "TpmVersion", &uTpmVersion, 2);
421 if (RT_FAILURE(rc))
422 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
423 N_("Configuration error: querying \"TpmVersion\" resulted in %Rrc"), rc);
424
425 switch (uTpmVersion)
426 {
427 case 1:
428 enmVersion = TPMLIB_TPM_VERSION_1_2;
429 pThis->enmVersion = TPMVERSION_1_2;
430 break;
431 case 2:
432 enmVersion = TPMLIB_TPM_VERSION_2;
433 pThis->enmVersion = TPMVERSION_2_0;
434 break;
435 default:
436 return PDMDrvHlpVMSetError(pDrvIns, VERR_NOT_SUPPORTED, RT_SRC_POS,
437 N_("Configuration error: \"TpmVersion\" %u is not supported"), uTpmVersion);
438 }
439
440 TPM_RESULT rcTpm = TPMLIB_ChooseTPMVersion(enmVersion);
441 if (rcTpm != TPM_SUCCESS)
442 return PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
443 N_("Failed to set the TPM version for the emulated TPM with %d"), rcTpm);
444
445 int cbBufferMax = 0;
446 rcTpm = TPMLIB_GetTPMProperty(TPMPROP_TPM_BUFFER_MAX, &cbBufferMax);
447 if (rcTpm != TPM_SUCCESS)
448 return PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
449 N_("Querying the maximum supported buffer size failed with %u"), rcTpm);
450
451 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "BufferSize", &pThis->cbBuffer, (uint32_t)cbBufferMax);
452 if (RT_FAILURE(rc))
453 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
454 N_("Configuration error: querying \"BufferSize\" resulted in %Rrc"), rc);
455
456 uint32_t cbBufferMin = 0;
457 uint32_t cbBuffer = TPMLIB_SetBufferSize(pThis->cbBuffer, &cbBufferMin, NULL /*max_size*/);
458 if (pThis->cbBuffer != cbBuffer)
459 return PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
460 N_("Failed to set buffer size (%u) of the emulated TPM with %u (min %u, max %d)"),
461 pThis->cbBuffer, cbBuffer, cbBufferMin, cbBufferMax);
462
463 struct libtpms_callbacks Callbacks;
464 Callbacks.sizeOfStruct = sizeof(Callbacks);
465 Callbacks.tpm_nvram_init = drvTpmEmuTpmsCbkNvRamInit;
466 Callbacks.tpm_nvram_loaddata = drvTpmEmuTpmsCbkNvRamLoadData;
467 Callbacks.tpm_nvram_storedata = drvTpmEmuTpmsCbkNvRamStoreData;
468 Callbacks.tpm_nvram_deletename = drvTpmEmuTpmsCbkNvRamDeleteName;
469 Callbacks.tpm_io_init = drvTpmEmuTpmsCbkIoInit;
470 Callbacks.tpm_io_getlocality = drvTpmEmuTpmsCbkIoGetLocality;
471 Callbacks.tpm_io_getphysicalpresence = drvTpmEmuTpmsCbkIoGetPhysicalPresence;
472 rcTpm = TPMLIB_RegisterCallbacks(&Callbacks);
473 if (rcTpm != TPM_SUCCESS)
474 return PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
475 N_("Failed to register callbacks with the TPM emulation: %u"),
476 rcTpm);
477
478 /* We can only have one instance of the TPM emulation and require the global variable for the callbacks unfortunately. */
479 g_pDrvTpmEmuTpms = pThis;
480 return VINF_SUCCESS;
481}
482
483
484/**
485 * TPM libtpms emulator driver registration record.
486 */
487const PDMDRVREG g_DrvTpmEmuTpms =
488{
489 /* u32Version */
490 PDM_DRVREG_VERSION,
491 /* szName */
492 "TpmEmuTpms",
493 /* szRCMod */
494 "",
495 /* szR0Mod */
496 "",
497 /* pszDescription */
498 "TPM emulation driver based on libtpms.",
499 /* fFlags */
500 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
501 /* fClass. */
502 PDM_DRVREG_CLASS_STREAM,
503 /* cMaxInstances */
504 1,
505 /* cbInstance */
506 sizeof(DRVTPMEMU),
507 /* pfnConstruct */
508 drvTpmEmuTpmsConstruct,
509 /* pfnDestruct */
510 NULL,
511 /* pfnRelocate */
512 NULL,
513 /* pfnIOCtl */
514 NULL,
515 /* pfnPowerOn */
516 drvTpmEmuTpmsPowerOn,
517 /* pfnReset */
518 drvTpmEmuTpmsReset,
519 /* pfnSuspend */
520 NULL,
521 /* pfnResume */
522 NULL,
523 /* pfnAttach */
524 NULL,
525 /* pfnDetach */
526 NULL,
527 /* pfnPowerOff */
528 drvTpmEmuTpmsPowerOff,
529 /* pfnSoftReset */
530 NULL,
531 /* u32EndVersion */
532 PDM_DRVREG_VERSION
533};
534
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use