VirtualBox

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

Last change on this file since 91703 was 91615, checked in by vboxsync, 3 years ago

Devices/Secureity/DrvTpmEmuTpms: Implement a VM reset callback, bugref:10075

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette