VirtualBox

source: vbox/trunk/src/VBox/Devices/Security/DrvTpmEmu.cpp@ 93115

Last change on this file since 93115 was 93115, checked in by vboxsync, 2 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.7 KB
Line 
1/* $Id: DrvTpmEmu.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * TPM emulator using a TCP/socket interface to talk to swtpm (https://github.com/stefanberger/swtpm).
4 */
5
6/*
7 * Copyright (C) 2021-2022 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/stream.h>
28#include <iprt/alloc.h>
29#include <iprt/pipe.h>
30#include <iprt/poll.h>
31#include <iprt/string.h>
32#include <iprt/semaphore.h>
33#include <iprt/socket.h>
34#include <iprt/tcp.h>
35#include <iprt/uuid.h>
36#include <iprt/json.h>
37
38#include <iprt/formats/tpm.h>
39
40#include "VBoxDD.h"
41
42
43/*********************************************************************************************************************************
44* Defined Constants And Macros *
45*********************************************************************************************************************************/
46
47
48/*********************************************************************************************************************************
49* Structures and Typedefs *
50*********************************************************************************************************************************/
51
52/** @name Protocol definitions to communicate with swtpm, taken from https://github.com/stefanberger/swtpm/blob/master/include/swtpm/tpm_ioctl.h
53 * @{ */
54/**
55 * Commands going over the control channel (big endian).
56 */
57typedef enum SWTPMCMD
58{
59 /** Not used. */
60 SWTPMCMD_INVALID = 0,
61 SWTPMCMD_GET_CAPABILITY,
62 SWTPMCMD_INIT,
63 SWTPMCMD_SHUTDOWN,
64 SWTPMCMD_GET_TPMESTABLISHED,
65 SWTPMCMD_SET_LOCALITY,
66 SWTPMCMD_HASH_START,
67 SWTPMCMD_HASH_DATA,
68 SWTPMCMD_HASH_END,
69 SWTPMCMD_CANCEL_TPM_CMD,
70 SWTPMCMD_STORE_VOLATILE,
71 SWTPMCMD_RESET_TPMESTABLISHED,
72 SWTPMCMD_GET_STATEBLOB,
73 SWTPMCMD_SET_STATEBLOB,
74 SWTPMCMD_STOP,
75 SWTPMCMD_GET_CONFIG,
76 SWTPMCMD_SET_DATAFD,
77 SWTPMCMD_SET_BUFFERSIZE,
78 SWTPMCMD_GET_INFO,
79 /** Blow the enum up to a 32bit size. */
80 SWTPMCMD_32BIT_HACK = 0x7fffffff
81} SWTPMCMD;
82
83
84/**
85 * Command/Response header.
86 */
87typedef union SWTPMHDR
88{
89 /** The command opcode. */
90 SWTPMCMD enmCmd;
91 /** The response result. */
92 uint32_t u32Resp;
93} SWTPMHDR;
94AssertCompileSize(SWTPMHDR, sizeof(uint32_t));
95/** Pointer to a command/response header. */
96typedef SWTPMHDR *PSWTPMHDR;
97/** Pointer to a const command/response header. */
98typedef const SWTPMHDR *PCSWTPMHDR;
99
100
101/**
102 * Additional command data for SWTPMCMD_INIT.
103 */
104typedef struct SWTPMCMDTPMINIT
105{
106 /** Additional flags */
107 uint32_t u32Flags;
108} SWTPMCMDTPMINIT;
109/** Pointer to a command data struct for SWTPMCMD_INIT. */
110typedef SWTPMCMDTPMINIT *PSWTPMCMDTPMINIT;
111/** Pointer to a const command data struct for SWTPMCMD_INIT. */
112typedef const SWTPMCMDTPMINIT *PCSWTPMCMDTPMINIT;
113
114
115/** @name Capabilities as returned by SWTPMCMD_INIT.
116 * @{ */
117#define SWTPMCMD_INIT_F_DELETE_VOLATILE RT_BIT_32(0);
118/** @} */
119
120
121/**
122 * Response data for a SWTPMCMD_GET_CAPABILITY command.
123 */
124typedef struct SWTPMRESPGETCAPABILITY
125{
126 /** The capabilities supported. */
127 uint32_t u32Caps;
128} SWTPMRESPGETCAPABILITY;
129/** Pointer to a response data struct for SWTPMCMD_GET_CAPABILITY. */
130typedef SWTPMRESPGETCAPABILITY *PSWTPMRESPGETCAPABILITY;
131/** Pointer to a const response data struct for SWTPMCMD_GET_CAPABILITY. */
132typedef const SWTPMRESPGETCAPABILITY *PCSWTPMRESPGETCAPABILITY;
133
134
135/** @name Capabilities as returned by SWTPMCMD_GET_CAPABILITY.
136 * @{ */
137#define SWTPM_CAP_INIT RT_BIT_32(0)
138#define SWTPM_CAP_SHUTDOWN RT_BIT_32(1)
139#define SWTPM_CAP_GET_TPMESTABLISHED RT_BIT_32(2)
140#define SWTPM_CAP_SET_LOCALITY RT_BIT_32(3)
141#define SWTPM_CAP_HASHING RT_BIT_32(4)
142#define SWTPM_CAP_CANCEL_TPM_CMD RT_BIT_32(5)
143#define SWTPM_CAP_STORE_VOLATILE RT_BIT_32(6)
144#define SWTPM_CAP_RESET_TPMESTABLISHED RT_BIT_32(7)
145#define SWTPM_CAP_GET_STATEBLOB RT_BIT_32(8)
146#define SWTPM_CAP_SET_STATEBLOB RT_BIT_32(9)
147#define SWTPM_CAP_STOP RT_BIT_32(10)
148#define SWTPM_CAP_GET_CONFIG RT_BIT_32(11)
149#define SWTPM_CAP_SET_DATAFD RT_BIT_32(12)
150#define SWTPM_CAP_SET_BUFFERSIZE RT_BIT_32(13)
151#define SWTPM_CAP_GET_INFO RT_BIT_32(14)
152#define SWTPM_CAP_SEND_COMMAND_HEADER RT_BIT_32(15)
153/** @} */
154
155
156/**
157 * Additional command data for SWTPMCMD_SET_LOCALITY.
158 */
159typedef struct SWTPMCMDSETLOCALITY
160{
161 /** The locality to set */
162 uint8_t bLoc;
163} SWTPMCMDSETLOCALITY;
164/** Pointer to a command data struct for SWTPMCMD_SET_LOCALITY. */
165typedef SWTPMCMDSETLOCALITY *PSWTPMCMDSETLOCALITY;
166/** Pointer to a const command data struct for SWTPMCMD_SET_LOCALITY. */
167typedef const SWTPMCMDSETLOCALITY *PCSWTPMCMDSETLOCALITY;
168
169
170/**
171 * Additional command data for SWTPMCMD_GET_CONFIG.
172 */
173typedef struct SWTPMCMDGETCONFIG
174{
175 /** Combination of SWTPM_GET_CONFIG_F_XXX. */
176 uint64_t u64Flags;
177 /** The offset where to start reading from. */
178 uint32_t u32Offset;
179 /** Some padding to a 8 byte alignment. */
180 uint32_t u32Padding;
181} SWTPMCMDGETCONFIG;
182/** Pointer to a response data struct for SWTPMCMD_GET_CONFIG. */
183typedef SWTPMCMDGETCONFIG *PSWTPMCMDGETCONFIG;
184/** Pointer to a const response data struct for SWTPMCMD_GET_CONFIG. */
185typedef const SWTPMCMDGETCONFIG *PCSWTPMCMDGETCONFIG;
186
187
188/** @name Flags for SWTPMCMDGETCONFIG::u64Flags.
189 * @{ */
190/** Return the TPM specification JSON object. */
191#define SWTPM_GET_CONFIG_F_TPM_SPECIFICATION RT_BIT_64(0)
192/** Return the TPM attributes JSON object. */
193#define SWTPM_GET_CONFIG_F_TPM_ATTRIBUTES RT_BIT_64(1)
194/** @} */
195
196
197/**
198 * Response data for a SWTPMCMD_GET_CONFIG command.
199 */
200typedef struct SWTPMRESPGETCONFIG
201{
202 /** Total size of the object in bytes. */
203 uint32_t cbTotal;
204 /** Size of the chunk returned in this response. */
205 uint32_t cbThis;
206} SWTPMRESPGETCONFIG;
207/** Pointer to a response data struct for SWTPMCMD_GET_CONFIG. */
208typedef SWTPMRESPGETCONFIG *PSWTPMRESPGETCONFIG;
209/** Pointer to a const response data struct for SWTPMCMD_GET_CONFIG. */
210typedef const SWTPMRESPGETCONFIG *PCSWTPMRESPGETCONFIG;
211
212
213/**
214 * Response data for a SWTPMCMD_GET_TPMESTABLISHED command.
215 */
216typedef struct SWTPMRESPGETTPMEST
217{
218 /** Flag whether the TPM established bit is set for the TPM. */
219 uint8_t fEst;
220} SWTPMRESPGETTPMEST;
221/** Pointer to a response data struct for SWTPMCMD_GET_TPMESTABLISHED. */
222typedef SWTPMRESPGETTPMEST *PSWTPMRESPGETTPMEST;
223/** Pointer to a const response data struct for SWTPMCMD_GET_TPMESTABLISHED. */
224typedef const SWTPMRESPGETTPMEST *PCSWTPMRESPGETTPMEST;
225
226
227/**
228 * Additional command data for SWTPMCMD_RESET_TPMESTABLISHED.
229 */
230typedef struct SWTPMCMDRSTEST
231{
232 /** The locality resetting trying to reset the established bit. */
233 uint8_t bLoc;
234} SWTPMCMDRSTEST;
235/** Pointer to a response data struct for SWTPMCMD_GET_TPMESTABLISHED. */
236typedef SWTPMCMDRSTEST *PSWTPMCMDRSTEST;
237/** Pointer to a const response data struct for SWTPMCMD_GET_TPMESTABLISHED. */
238typedef const SWTPMCMDRSTEST *PCSWTPMCMDRSTEST;
239
240
241/**
242 * Additional command data for SWTPMCMD_SET_BUFFERSIZE.
243 */
244typedef struct SWTPMCMDSETBUFSZ
245{
246 /** The buffer size to set, 0 to query for the currently used buffer size. */
247 uint32_t cbBuffer;
248} SWTPMCMDSETBUFSZ;
249/** Pointer to a command data struct for SWTPMCMD_SET_BUFFERSIZE. */
250typedef SWTPMCMDSETBUFSZ *PSWTPMCMDSETBUFSZ;
251/** Pointer to a const command data struct for SWTPMCMD_SET_BUFFERSIZE. */
252typedef const SWTPMCMDSETBUFSZ *PCSWTPMCMDSETBUFSZ;
253
254
255/**
256 * Response data for a SWTPMCMD_SET_BUFFERSIZE command.
257 */
258typedef struct SWTPMRESPSETBUFSZ
259{
260 /** Buffer size in use. */
261 uint32_t cbBuffer;
262 /** Minimum supported buffer size. */
263 uint32_t cbBufferMin;
264 /** Maximum supported buffer size. */
265 uint32_t cbBufferMax;
266} SWTPMRESPSETBUFSZ;
267/** Pointer to a response data struct for SWTPMCMD_SET_BUFFERSIZE. */
268typedef SWTPMRESPSETBUFSZ *PSWTPMRESPSETBUFSZ;
269/** Pointer to a const response data struct for SWTPMCMD_SET_BUFFERSIZE. */
270typedef const SWTPMRESPSETBUFSZ *PCSWTPMRESPSETBUFSZ;
271
272
273/**
274 * TPM emulator driver instance data.
275 *
276 * @implements PDMITPMCONNECTOR
277 */
278typedef struct DRVTPMEMU
279{
280 /** The stream interface. */
281 PDMITPMCONNECTOR ITpmConnector;
282 /** Pointer to the driver instance. */
283 PPDMDRVINS pDrvIns;
284
285 /** Socket handle for the control connection. */
286 RTSOCKET hSockCtrl;
287 /** Socket handle for the data connection. */
288 RTSOCKET hSockData;
289
290 /** Currently set locality. */
291 uint8_t bLoc;
292
293 /** TPM version offered by the emulator. */
294 TPMVERSION enmTpmVers;
295 /** Capabilities offered by the TPM emulator. */
296 uint32_t fCaps;
297 /** Buffer size for the emulated TPM. */
298 uint32_t cbBuffer;
299} DRVTPMEMU;
300/** Pointer to the TPM emulator instance data. */
301typedef DRVTPMEMU *PDRVTPMEMU;
302
303/** The special no current locality selected value. */
304#define TPM_NO_LOCALITY_SELECTED 0xff
305
306
307/*********************************************************************************************************************************
308* Internal Functions *
309*********************************************************************************************************************************/
310
311/**
312 * Executes the given command over the control connection to the TPM emulator.
313 *
314 * @returns VBox status code.
315 * @param pThis Pointer to the TPM emulator driver instance data.
316 * @param enmCmd The command to execute.
317 * @param pvCmd Additional command data to send.
318 * @param cbCmd Size of the additional command data in bytes.
319 * @param pu32Resp Where to store the response code from the reply.
320 * @param pvResp Where to store additional resposne data.
321 * @param cbResp Size of the Response data in bytes (excluding the response status code which is implicit).
322 * @param cMillies Number of milliseconds to wait before aborting the command with a timeout error.
323 *
324 * @note This method can return success even though the request at such failed, check the content of pu32Resp!
325 */
326static int drvTpmEmuExecCtrlCmdEx(PDRVTPMEMU pThis, SWTPMCMD enmCmd, const void *pvCmd, size_t cbCmd, uint32_t *pu32Resp,
327 void *pvResp, size_t cbResp, RTMSINTERVAL cMillies)
328{
329 SWTPMHDR Hdr;
330 RTSGBUF SgBuf;
331 RTSGSEG aSegs[2];
332 uint32_t cSegs = 1;
333
334 Hdr.enmCmd = (SWTPMCMD)RT_H2BE_U32(enmCmd);
335 aSegs[0].pvSeg = &Hdr;
336 aSegs[0].cbSeg = sizeof(Hdr);
337 if (cbCmd)
338 {
339 cSegs++;
340 aSegs[1].pvSeg = (void *)pvCmd;
341 aSegs[1].cbSeg = cbCmd;
342 }
343
344 RTSgBufInit(&SgBuf, &aSegs[0], cSegs);
345 int rc = RTSocketSgWrite(pThis->hSockCtrl, &SgBuf);
346 if (RT_SUCCESS(rc))
347 {
348 rc = RTSocketSelectOne(pThis->hSockCtrl, cMillies);
349 if (RT_SUCCESS(rc))
350 {
351 uint32_t u32Resp = 0;
352 rc = RTSocketRead(pThis->hSockCtrl, &u32Resp, sizeof(u32Resp), NULL /*pcbRead*/);
353 if (RT_SUCCESS(rc))
354 {
355 *pu32Resp = RT_BE2H_U32(u32Resp);
356 if (*pu32Resp == 0)
357 {
358 if (cbResp)
359 rc = RTSocketRead(pThis->hSockCtrl, pvResp, cbResp, NULL /*pcbRead*/);
360 }
361 else
362 rc = VERR_NET_IO_ERROR;
363 }
364 }
365 }
366
367 return rc;
368}
369
370
371/**
372 * Continue receiving a response from a previous call of drvTpmEmuExecCtrlCmdEx() or
373 * drvTpmEmuExecCtrlCmdNoPayload().
374 *
375 * @param pThis Pointer to the TPM emulator driver instance data.
376 * @param enmCmd The command to execute.
377 * @param pvResp Where to store additional resposne data.
378 * @param cbResp Size of the additional response data in bytes.
379 * @param cMillies Number of milliseconds to wait before aborting the receive with a timeout error.
380 */
381static int drvTpmEmuExecCtrlCmdRespCont(PDRVTPMEMU pThis, void *pvResp, size_t cbResp, RTMSINTERVAL cMillies)
382{
383 int rc = RTSocketSelectOne(pThis->hSockCtrl, cMillies);
384 if (RT_SUCCESS(rc))
385 rc = RTSocketRead(pThis->hSockCtrl, pvResp, cbResp, NULL /*pcbRead*/);
386
387 return rc;
388}
389
390
391/**
392 * Executes the given command over the control connection to the TPM emulator - variant with no command payload.
393 *
394 * @returns VBox status code.
395 * @retval VERR_NET_IO_ERROR if the executed command returned an error in the response status field.
396 * @param pThis Pointer to the TPM emulator driver instance data.
397 * @param enmCmd The command to execute.
398 * @param pvResp Where to store additional resposne data.
399 * @param cbResp Size of the Response data in bytes (excluding the response status code which is implicit).
400 * @param cMillies Number of milliseconds to wait before aborting the command with a timeout error.
401 */
402static int drvTpmEmuExecCtrlCmdNoPayload(PDRVTPMEMU pThis, SWTPMCMD enmCmd, void *pvResp, size_t cbResp, RTMSINTERVAL cMillies)
403{
404 uint32_t u32Resp = 0;
405 int rc = drvTpmEmuExecCtrlCmdEx(pThis, enmCmd, NULL /*pvCmd*/, 0 /*cbCmd*/, &u32Resp,
406 pvResp, cbResp, cMillies);
407 if (RT_SUCCESS(rc))
408 {
409 if (u32Resp != 0)
410 rc = VERR_NET_IO_ERROR;
411 }
412
413 return rc;
414}
415
416
417/**
418 * Executes the given command over the control connection to the TPM emulator - variant with no response payload other than the result.
419 *
420 * @returns VBox status code.
421 * @retval VERR_NET_IO_ERROR if the executed command returned an error in the response status field.
422 * @param pThis Pointer to the TPM emulator driver instance data.
423 * @param enmCmd The command to execute.
424 * @param pvCmd Additional command data to send.
425 * @param cbCmd Size of the additional command data in bytes.
426 * @param pu32Resp Where to store the response code from the reply.
427 * @param cMillies Number of milliseconds to wait before aborting the command with a timeout error.
428 */
429static int drvTpmEmuExecCtrlCmdNoResp(PDRVTPMEMU pThis, SWTPMCMD enmCmd, const void *pvCmd, size_t cbCmd, uint32_t *pu32Resp,
430 RTMSINTERVAL cMillies)
431{
432 return drvTpmEmuExecCtrlCmdEx(pThis, enmCmd, pvCmd, cbCmd, pu32Resp,
433 NULL /*pvResp*/, 0 /*cbResp*/, cMillies);
434}
435
436
437/**
438 * Executes the given command over the control connection to the TPM emulator - variant with no command and response payload.
439 *
440 * @returns VBox status code.
441 * @retval VERR_NET_IO_ERROR if the executed command returned an error in the response status field.
442 * @param pThis Pointer to the TPM emulator driver instance data.
443 * @param enmCmd The command to execute.
444 * @param cMillies Number of milliseconds to wait before aborting the command with a timeout error.
445 */
446static int drvTpmEmuExecCtrlCmdNoPayloadAndResp(PDRVTPMEMU pThis, SWTPMCMD enmCmd, RTMSINTERVAL cMillies)
447{
448 uint32_t u32Resp = 0;
449 int rc = drvTpmEmuExecCtrlCmdEx(pThis, enmCmd, NULL /*pvCmd*/, 0 /*cbCmd*/, &u32Resp,
450 NULL /*pvResp*/, 0 /*cbResp*/, cMillies);
451 if (RT_SUCCESS(rc))
452 {
453 if (u32Resp != 0)
454 rc = VERR_NET_IO_ERROR;
455 }
456
457 return rc;
458}
459
460
461/**
462 * Queries the version of the TPM offered by the remote emulator.
463 *
464 * @returns VBox status code.
465 * @param pThis Pointer to the TPM emulator driver instance data.
466 */
467static int drvTpmEmuQueryTpmVersion(PDRVTPMEMU pThis)
468{
469 SWTPMCMDGETCONFIG Cmd;
470 SWTPMRESPGETCONFIG Resp;
471 uint8_t abData[_4K];
472 uint32_t u32Resp = 0;
473
474 RT_ZERO(Cmd); RT_ZERO(Resp);
475 Cmd.u64Flags = RT_H2BE_U64(SWTPM_GET_CONFIG_F_TPM_SPECIFICATION);
476 Cmd.u32Offset = 0;
477 int rc = drvTpmEmuExecCtrlCmdEx(pThis, SWTPMCMD_GET_INFO, &Cmd, sizeof(Cmd), &u32Resp,
478 &Resp, sizeof(Resp), RT_MS_10SEC);
479 if (RT_SUCCESS(rc))
480 {
481 /*
482 * Currently it is not necessary to get the information in chunks, a single
483 * transaction is enough. To fend off future versions of swtpm requiring this
484 * we return an error here if the total length is not equal to the length of the chunk.
485 */
486 if (RT_BE2H_U32(Resp.cbTotal) == RT_BE2H_U32(Resp.cbThis))
487 {
488 /* Fetch the response body. */
489 rc = drvTpmEmuExecCtrlCmdRespCont(pThis, &abData[0], RT_BE2H_U32(Resp.cbThis), RT_MS_10SEC);
490 if (RT_SUCCESS(rc))
491 {
492 RTJSONVAL hJsonVal = NIL_RTJSONVAL;
493 rc = RTJsonParseFromBuf(&hJsonVal, &abData[0], RT_BE2H_U32(Resp.cbThis), NULL /*pErrInfo*/);
494 if (RT_SUCCESS(rc))
495 {
496 RTJSONVAL hJsonTpmSpec = NIL_RTJSONVAL;
497 rc = RTJsonValueQueryByName(hJsonVal, "TPMSpecification", &hJsonTpmSpec);
498 if (RT_SUCCESS(rc))
499 {
500 RTJSONVAL hJsonTpmFam = NIL_RTJSONVAL;
501 rc = RTJsonValueQueryByName(hJsonTpmSpec, "family", &hJsonTpmFam);
502 if (RT_SUCCESS(rc))
503 {
504 const char *pszFam = NULL;
505 rc = RTJsonValueQueryString(hJsonTpmFam, &pszFam);
506 if (RT_SUCCESS(rc))
507 {
508 if (!RTStrCmp(pszFam, "1.2"))
509 pThis->enmTpmVers = TPMVERSION_1_2;
510 else if (!RTStrCmp(pszFam, "2.0"))
511 pThis->enmTpmVers = TPMVERSION_2_0;
512 else
513 pThis->enmTpmVers = TPMVERSION_UNKNOWN;
514 }
515
516 RTJsonValueRelease(hJsonTpmFam);
517 }
518
519 RTJsonValueRelease(hJsonTpmSpec);
520 }
521
522 RTJsonValueRelease(hJsonVal);
523 }
524 }
525 }
526 else
527 rc = VERR_NOT_SUPPORTED;
528 }
529
530 return rc;
531}
532
533
534/**
535 * Queries the capabilities of the remote TPM emulator and verifies that
536 * it offers everything we require for operation.
537 *
538 * @returns VBox status code.
539 * @param pThis Pointer to the TPM emulator driver instance data.
540 */
541static int drvTpmEmuQueryCaps(PDRVTPMEMU pThis)
542{
543 SWTPMRESPGETCAPABILITY Resp;
544 int rc = drvTpmEmuExecCtrlCmdNoPayload(pThis, SWTPMCMD_GET_CAPABILITY, &Resp, sizeof(Resp), RT_MS_10SEC);
545 if (RT_SUCCESS(rc))
546 pThis->fCaps = RT_BE2H_U32(Resp.u32Caps);
547
548 return rc;
549}
550
551
552/**
553 * Queries the maximum supported buffer size by the emulation.
554 *
555 * @returns VBox status code.
556 * @param pThis Pointer to the TPM emulator driver instance data.
557 * @param pcbBufferMax Where to store the maximum supported buffer size on success.
558 */
559static int drvTpmEmuQueryBufferSzMax(PDRVTPMEMU pThis, uint32_t *pcbBufferMax)
560{
561 SWTPMCMDSETBUFSZ Cmd;
562 SWTPMRESPSETBUFSZ Resp;
563 uint32_t u32Resp = 0;
564
565 RT_ZERO(Cmd); RT_ZERO(Resp);
566 Cmd.cbBuffer = RT_H2BE_U32(0);
567 int rc = drvTpmEmuExecCtrlCmdEx(pThis, SWTPMCMD_SET_BUFFERSIZE, &Cmd, sizeof(Cmd), &u32Resp,
568 &Resp, sizeof(Resp), RT_MS_10SEC);
569 if (RT_SUCCESS(rc))
570 {
571 if (u32Resp == 0)
572 *pcbBufferMax = RT_BE2H_U32(Resp.cbBufferMax);
573 else
574 rc = VERR_NET_IO_ERROR;
575 }
576
577 return rc;
578}
579
580
581/**
582 * Queries the maximum supported buffer size by the emulation.
583 *
584 * @returns VBox status code.
585 * @param pThis Pointer to the TPM emulator driver instance data.
586 * @param cbBuffer The buffer size to set.
587 */
588static int drvTpmEmuSetBufferSz(PDRVTPMEMU pThis, uint32_t cbBuffer)
589{
590 SWTPMCMDSETBUFSZ Cmd;
591 SWTPMRESPSETBUFSZ Resp;
592 uint32_t u32Resp = 0;
593
594 RT_ZERO(Cmd); RT_ZERO(Resp);
595 Cmd.cbBuffer = RT_H2BE_U32(cbBuffer);
596 int rc = drvTpmEmuExecCtrlCmdEx(pThis, SWTPMCMD_SET_BUFFERSIZE, &Cmd, sizeof(Cmd), &u32Resp,
597 &Resp, sizeof(Resp), RT_MS_10SEC);
598 if ( RT_SUCCESS(rc)
599 && u32Resp != 0)
600 rc = VERR_NET_IO_ERROR;
601
602 return rc;
603}
604
605
606/**
607 * Sets the given locality for the emulated TPM.
608 *
609 * @returns VBox status code.
610 * @param pThis Pointer to the TPM emulator driver instance data.
611 * @param bLoc The locality to set.
612 */
613static int drvTpmEmuSetLocality(PDRVTPMEMU pThis, uint8_t bLoc)
614{
615 SWTPMCMDSETLOCALITY Cmd;
616 uint32_t u32Resp = 0;
617
618 Cmd.bLoc = bLoc;
619 int rc = drvTpmEmuExecCtrlCmdNoResp(pThis, SWTPMCMD_SET_LOCALITY, &Cmd, sizeof(Cmd), &u32Resp, RT_MS_10SEC);
620 if ( RT_SUCCESS(rc)
621 && u32Resp != 0)
622 rc = VERR_NET_IO_ERROR;
623
624 if (RT_SUCCESS(rc))
625 pThis->bLoc = bLoc;
626
627 return rc;
628}
629
630
631/** @interface_method_impl{PDMITPMCONNECTOR,pfnGetVersion} */
632static DECLCALLBACK(TPMVERSION) drvTpmEmuGetVersion(PPDMITPMCONNECTOR pInterface)
633{
634 PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
635 return pThis->enmTpmVers;
636}
637
638
639/** @interface_method_impl{PDMITPMCONNECTOR,pfnGetLocalityMax} */
640static DECLCALLBACK(uint32_t) drvTpmEmuGetLocalityMax(PPDMITPMCONNECTOR pInterface)
641{
642 RT_NOREF(pInterface);
643 return 4;
644}
645
646
647/** @interface_method_impl{PDMITPMCONNECTOR,pfnGetBufferSize} */
648static DECLCALLBACK(uint32_t) drvTpmEmuGetBufferSize(PPDMITPMCONNECTOR pInterface)
649{
650 PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
651 return pThis->cbBuffer;
652}
653
654
655/** @interface_method_impl{PDMITPMCONNECTOR,pfnGetEstablishedFlag} */
656static DECLCALLBACK(bool) drvTpmEmuGetEstablishedFlag(PPDMITPMCONNECTOR pInterface)
657{
658 PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
659
660 SWTPMRESPGETTPMEST Resp;
661 int rc = drvTpmEmuExecCtrlCmdNoPayload(pThis, SWTPMCMD_GET_TPMESTABLISHED, &Resp, sizeof(Resp), RT_MS_10SEC);
662 if (RT_SUCCESS(rc)
663 && Resp.fEst != 0)
664 return true;
665
666 return false;
667}
668
669
670/** @interface_method_impl{PDMITPMCONNECTOR,pfnResetEstablishedFlag} */
671static DECLCALLBACK(int) drvTpmEmuResetEstablishedFlag(PPDMITPMCONNECTOR pInterface, uint8_t bLoc)
672{
673 PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
674
675 SWTPMCMDRSTEST Cmd;
676 uint32_t u32Resp = 0;
677
678 Cmd.bLoc = bLoc;
679 int rc = drvTpmEmuExecCtrlCmdNoResp(pThis, SWTPMCMD_RESET_TPMESTABLISHED, &Cmd, sizeof(Cmd), &u32Resp, RT_MS_10SEC);
680 if ( RT_SUCCESS(rc)
681 && u32Resp != 0)
682 rc = VERR_NET_IO_ERROR;
683
684 return rc;
685}
686
687
688/** @interface_method_impl{PDMITPMCONNECTOR,pfnCmdExec} */
689static DECLCALLBACK(int) drvTpmEmuCmdExec(PPDMITPMCONNECTOR pInterface, uint8_t bLoc, const void *pvCmd, size_t cbCmd, void *pvResp, size_t cbResp)
690{
691 PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
692
693 int rc = VINF_SUCCESS;
694 if (pThis->bLoc != bLoc)
695 rc = drvTpmEmuSetLocality(pThis, bLoc);
696
697 if (RT_SUCCESS(rc))
698 {
699 rc = RTSocketWrite(pThis->hSockData, pvCmd, cbCmd);
700 if (RT_SUCCESS(rc))
701 {
702 rc = RTSocketSelectOne(pThis->hSockData, RT_MS_10SEC);
703 if (RT_SUCCESS(rc))
704 {
705 /* Read the response header in first. */
706 TPMRESPHDR RespHdr;
707 rc = RTSocketRead(pThis->hSockData, &RespHdr, sizeof(RespHdr), NULL /*pcbRead*/);
708 if (RT_SUCCESS(rc))
709 {
710 size_t cbHdrResp = RTTpmRespGetSz(&RespHdr);
711 if (cbHdrResp <= cbResp - sizeof(RespHdr))
712 {
713 memcpy(pvResp, &RespHdr, sizeof(RespHdr));
714
715 if (cbHdrResp > sizeof(RespHdr))
716 rc = RTSocketRead(pThis->hSockData, (uint8_t *)pvResp + sizeof(RespHdr), cbHdrResp - sizeof(RespHdr),
717 NULL /*pcbRead*/);
718 }
719 else
720 rc = VERR_BUFFER_OVERFLOW;
721 }
722 }
723 }
724 }
725
726 return rc;
727}
728
729
730/** @interface_method_impl{PDMITPMCONNECTOR,pfnCmdCancel} */
731static DECLCALLBACK(int) drvTpmEmuCmdCancel(PPDMITPMCONNECTOR pInterface)
732{
733 PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
734
735 return drvTpmEmuExecCtrlCmdNoPayloadAndResp(pThis, SWTPMCMD_CANCEL_TPM_CMD, RT_MS_10SEC);
736}
737
738
739/** @interface_method_impl{PDMIBASE,pfnQueryInterface} */
740static DECLCALLBACK(void *) drvTpmEmuQueryInterface(PPDMIBASE pInterface, const char *pszIID)
741{
742 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
743 PDRVTPMEMU pThis = PDMINS_2_DATA(pDrvIns, PDRVTPMEMU);
744 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
745 PDMIBASE_RETURN_INTERFACE(pszIID, PDMITPMCONNECTOR, &pThis->ITpmConnector);
746 return NULL;
747}
748
749
750/* -=-=-=-=- PDMDRVREG -=-=-=-=- */
751
752/**
753 * @interface_method_impl{PDMDRVREG,pfnPowerOn}
754 */
755static DECLCALLBACK(void) drvTpmEmuPowerOn(PPDMDRVINS pDrvIns)
756{
757 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
758 PDRVTPMEMU pThis = PDMINS_2_DATA(pDrvIns, PDRVTPMEMU);
759
760 SWTPMCMDTPMINIT Cmd;
761 uint32_t u32Resp = 0;
762
763 RT_ZERO(Cmd);
764 Cmd.u32Flags = 0;
765 int rc = drvTpmEmuExecCtrlCmdEx(pThis, SWTPMCMD_INIT, &Cmd, sizeof(Cmd), &u32Resp,
766 NULL, 0, RT_MS_10SEC);
767 if (RT_FAILURE(rc))
768 PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, "Failed to startup the TPM with %Rrc", rc);
769}
770
771
772/**
773 * @interface_method_impl{PDMDRVREG,pfnPowerOff}
774 */
775static DECLCALLBACK(void) drvTpmEmuPowerOff(PPDMDRVINS pDrvIns)
776{
777 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
778 PDRVTPMEMU pThis = PDMINS_2_DATA(pDrvIns, PDRVTPMEMU);
779
780 int rc = drvTpmEmuExecCtrlCmdNoPayload(pThis, SWTPMCMD_SHUTDOWN, NULL, 0, RT_MS_10SEC);
781 if (RT_FAILURE(rc))
782 PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, "Failed to shutdown the TPM with %Rrc", rc);
783}
784
785
786/** @copydoc FNPDMDRVDESTRUCT */
787static DECLCALLBACK(void) drvTpmEmuDestruct(PPDMDRVINS pDrvIns)
788{
789 PDRVTPMEMU pThis = PDMINS_2_DATA(pDrvIns, PDRVTPMEMU);
790 LogFlow(("%s\n", __FUNCTION__));
791 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
792
793 if (pThis->hSockCtrl != NIL_RTSOCKET)
794 {
795 int rc = RTSocketShutdown(pThis->hSockCtrl, true /* fRead */, true /* fWrite */);
796 AssertRC(rc);
797
798 rc = RTSocketClose(pThis->hSockCtrl);
799 AssertRC(rc); RT_NOREF(rc);
800
801 pThis->hSockCtrl = NIL_RTSOCKET;
802 }
803
804 if (pThis->hSockData != NIL_RTSOCKET)
805 {
806 int rc = RTSocketShutdown(pThis->hSockData, true /* fRead */, true /* fWrite */);
807 AssertRC(rc);
808
809 rc = RTSocketClose(pThis->hSockData);
810 AssertRC(rc); RT_NOREF(rc);
811
812 pThis->hSockCtrl = NIL_RTSOCKET;
813 }
814}
815
816
817/** @copydoc FNPDMDRVCONSTRUCT */
818static DECLCALLBACK(int) drvTpmEmuConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
819{
820 RT_NOREF(fFlags);
821 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
822 PDRVTPMEMU pThis = PDMINS_2_DATA(pDrvIns, PDRVTPMEMU);
823 PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
824
825 /*
826 * Init the static parts.
827 */
828 pThis->pDrvIns = pDrvIns;
829 pThis->hSockCtrl = NIL_RTSOCKET;
830 pThis->hSockData = NIL_RTSOCKET;
831 pThis->enmTpmVers = TPMVERSION_UNKNOWN;
832 pThis->bLoc = TPM_NO_LOCALITY_SELECTED;
833
834 /* IBase */
835 pDrvIns->IBase.pfnQueryInterface = drvTpmEmuQueryInterface;
836 /* ITpmConnector */
837 pThis->ITpmConnector.pfnGetVersion = drvTpmEmuGetVersion;
838 pThis->ITpmConnector.pfnGetLocalityMax = drvTpmEmuGetLocalityMax;
839 pThis->ITpmConnector.pfnGetBufferSize = drvTpmEmuGetBufferSize;
840 pThis->ITpmConnector.pfnGetEstablishedFlag = drvTpmEmuGetEstablishedFlag;
841 pThis->ITpmConnector.pfnResetEstablishedFlag = drvTpmEmuResetEstablishedFlag;
842 pThis->ITpmConnector.pfnCmdExec = drvTpmEmuCmdExec;
843 pThis->ITpmConnector.pfnCmdCancel = drvTpmEmuCmdCancel;
844
845 /*
846 * Validate and read the configuration.
847 */
848 PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "Location|BufferSize", "");
849
850 char szLocation[_1K];
851 int rc = pHlp->pfnCFGMQueryString(pCfg, "Location", &szLocation[0], sizeof(szLocation));
852 if (RT_FAILURE(rc))
853 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
854 N_("Configuration error: querying \"Location\" resulted in %Rrc"), rc);
855
856 /*
857 * Create/Open the socket.
858 */
859 char *pszPort = strchr(szLocation, ':');
860 if (!pszPort)
861 return PDMDrvHlpVMSetError(pDrvIns, VERR_NOT_FOUND, RT_SRC_POS,
862 N_("DrvTpmEmu#%d: The location misses the port to connect to"),
863 pDrvIns->iInstance);
864
865 *pszPort = '\0'; /* Overwrite temporarily to avoid copying the hostname into a temporary buffer. */
866 uint32_t uPort = 0;
867 rc = RTStrToUInt32Ex(pszPort + 1, NULL, 10, &uPort);
868 if (RT_FAILURE(rc))
869 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
870 N_("DrvTpmEmu#%d: The port part of the location is not a numerical value"),
871 pDrvIns->iInstance);
872
873 rc = RTTcpClientConnect(szLocation, uPort, &pThis->hSockCtrl);
874 *pszPort = ':'; /* Restore delimiter before checking the status. */
875 if (RT_FAILURE(rc))
876 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
877 N_("DrvTpmEmu#%d failed to connect to control socket %s"),
878 pDrvIns->iInstance, szLocation);
879
880 rc = drvTpmEmuQueryCaps(pThis);
881 if (RT_FAILURE(rc))
882 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
883 N_("DrvTpmEmu#%d failed to query capabilities offered by %s"),
884 pDrvIns->iInstance, szLocation);
885
886 if (!(pThis->fCaps & SWTPM_CAP_GET_CONFIG))
887 return PDMDrvHlpVMSetError(pDrvIns, VERR_NOT_SUPPORTED, RT_SRC_POS,
888 N_("DrvTpmEmu#%d Emulated TPM at '%s' misses the GET_CONFIG capability"),
889 pDrvIns->iInstance, szLocation);
890
891 rc = drvTpmEmuQueryTpmVersion(pThis);
892 if (RT_FAILURE(rc))
893 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
894 N_("DrvTpmEmu#%d failed to query TPM version from %s"),
895 pDrvIns->iInstance, szLocation);
896
897 if (pThis->enmTpmVers == TPMVERSION_UNKNOWN)
898 return PDMDrvHlpVMSetError(pDrvIns, VERR_NOT_SUPPORTED, RT_SRC_POS,
899 N_("DrvTpmEmu#%d Emulated TPM version of %s is not supported"),
900 pDrvIns->iInstance, szLocation);
901
902 const char *pszTpmVers = NULL;
903 uint32_t fCapsReq = SWTPM_CAP_INIT | SWTPM_CAP_SHUTDOWN | SWTPM_CAP_GET_TPMESTABLISHED
904 | SWTPM_CAP_SET_LOCALITY | SWTPM_CAP_CANCEL_TPM_CMD | SWTPM_CAP_GET_STATEBLOB
905 | SWTPM_CAP_SET_STATEBLOB | SWTPM_CAP_STOP | SWTPM_CAP_SET_BUFFERSIZE;
906 switch (pThis->enmTpmVers)
907 {
908 case TPMVERSION_1_2:
909 /* No additional capabilities needed. */
910 pszTpmVers = "1.2";
911 break;
912 case TPMVERSION_2_0:
913 fCapsReq |= SWTPM_CAP_RESET_TPMESTABLISHED;
914 pszTpmVers = "2.0";
915 break;
916 default:
917 AssertMsgFailedReturn(("DrvTpmEmu#%d Emulated TPM version %d is not correctly handled", pDrvIns->iInstance, pThis->enmTpmVers),
918 VERR_INVALID_STATE);
919 }
920
921 if ((pThis->fCaps & fCapsReq) != fCapsReq)
922 return PDMDrvHlpVMSetError(pDrvIns, VERR_NOT_SUPPORTED, RT_SRC_POS,
923 N_("DrvTpmEmu#%d Emulated TPM version of %s does not offer required set of capabilities (%#x requested vs. %#x offered)"),
924 pDrvIns->iInstance, szLocation, fCapsReq, pThis->fCaps);
925
926 uint32_t cbBufferMax = 0;
927 rc = drvTpmEmuQueryBufferSzMax(pThis, &cbBufferMax);
928 if (RT_FAILURE(rc))
929 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
930 N_("DrvTpmEmu#%d failed to query maximum buffer size from %s"),
931 pDrvIns->iInstance, szLocation);
932
933 /* Configure the buffer size. */
934 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "BufferSize", &pThis->cbBuffer, cbBufferMax);
935 if (RT_FAILURE(rc))
936 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
937 N_("Configuration error: querying \"BufferSize\" resulted in %Rrc"), rc);
938
939 /* Set the buffer size. */
940 rc = drvTpmEmuSetBufferSz(pThis, pThis->cbBuffer);
941 if (RT_FAILURE(rc))
942 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
943 N_("DrvTpmEmu#%d failed to set buffer size to %u for %s"),
944 pDrvIns->iInstance, pThis->cbBuffer, szLocation);
945
946 /* Connect the data channel now. */
947 /** @todo Allow configuring a different port. */
948 *pszPort = '\0'; /* Overwrite temporarily to avoid copying the hostname into a temporary buffer. */
949 rc = RTTcpClientConnect(szLocation, uPort + 1, &pThis->hSockData);
950 *pszPort = ':'; /* Restore delimiter before checking the status. */
951 if (RT_FAILURE(rc))
952 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
953 N_("DrvTpmEmu#%d failed to connect to data socket %s"),
954 pDrvIns->iInstance, szLocation);
955
956 LogRel(("DrvTpmEmu#%d: Connected to %s, emulating TPM version %s\n", pDrvIns->iInstance, szLocation, pszTpmVers));
957 return VINF_SUCCESS;
958}
959
960
961/**
962 * TPM emulator driver registration record.
963 */
964const PDMDRVREG g_DrvTpmEmu =
965{
966 /* u32Version */
967 PDM_DRVREG_VERSION,
968 /* szName */
969 "TpmEmu",
970 /* szRCMod */
971 "",
972 /* szR0Mod */
973 "",
974 /* pszDescription */
975 "TPM emulator driver.",
976 /* fFlags */
977 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
978 /* fClass. */
979 PDM_DRVREG_CLASS_STREAM,
980 /* cMaxInstances */
981 ~0U,
982 /* cbInstance */
983 sizeof(DRVTPMEMU),
984 /* pfnConstruct */
985 drvTpmEmuConstruct,
986 /* pfnDestruct */
987 drvTpmEmuDestruct,
988 /* pfnRelocate */
989 NULL,
990 /* pfnIOCtl */
991 NULL,
992 /* pfnPowerOn */
993 drvTpmEmuPowerOn,
994 /* pfnReset */
995 NULL,
996 /* pfnSuspend */
997 NULL,
998 /* pfnResume */
999 NULL,
1000 /* pfnAttach */
1001 NULL,
1002 /* pfnDetach */
1003 NULL,
1004 /* pfnPowerOff */
1005 drvTpmEmuPowerOff,
1006 /* pfnSoftReset */
1007 NULL,
1008 /* u32EndVersion */
1009 PDM_DRVREG_VERSION
1010};
1011
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use