VirtualBox

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

Last change on this file was 106259, checked in by vboxsync, 2 months ago

Devices/Security: Query the buffer size of the device above and use that to set the buffer size in libtpms, bugref:10772

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