VirtualBox

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

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

Devices/Security/DrvTpmEmuTpms: Straighten the NVRAM handling code now that NVRAM states are handled differently, bugref:10075

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