VirtualBox

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

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

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.0 KB
Line 
1/* $Id: DrvTpmEmu.cpp 98103 2023-01-17 14:15: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-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_DRV_TPM_EMU
33#include <VBox/vmm/pdmdrv.h>
34#include <VBox/vmm/pdmtpmifs.h>
35#include <iprt/assert.h>
36#include <iprt/file.h>
37#include <iprt/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
295 /** Socket handle for the control connection. */
296 RTSOCKET hSockCtrl;
297 /** Socket handle for the data connection. */
298 RTSOCKET hSockData;
299
300 /** Currently set locality. */
301 uint8_t bLoc;
302
303 /** TPM version offered by the emulator. */
304 TPMVERSION enmTpmVers;
305 /** Capabilities offered by the TPM emulator. */
306 uint32_t fCaps;
307 /** Buffer size for the emulated TPM. */
308 uint32_t cbBuffer;
309} DRVTPMEMU;
310/** Pointer to the TPM emulator instance data. */
311typedef DRVTPMEMU *PDRVTPMEMU;
312
313/** The special no current locality selected value. */
314#define TPM_NO_LOCALITY_SELECTED 0xff
315
316
317/*********************************************************************************************************************************
318* Internal Functions *
319*********************************************************************************************************************************/
320
321/**
322 * Executes the given command over the control connection to the TPM emulator.
323 *
324 * @returns VBox status code.
325 * @param pThis Pointer to the TPM emulator driver instance data.
326 * @param enmCmd The command to execute.
327 * @param pvCmd Additional command data to send.
328 * @param cbCmd Size of the additional command data in bytes.
329 * @param pu32Resp Where to store the response code from the reply.
330 * @param pvResp Where to store additional resposne data.
331 * @param cbResp Size of the Response data in bytes (excluding the response status code which is implicit).
332 * @param cMillies Number of milliseconds to wait before aborting the command with a timeout error.
333 *
334 * @note This method can return success even though the request at such failed, check the content of pu32Resp!
335 */
336static int drvTpmEmuExecCtrlCmdEx(PDRVTPMEMU pThis, SWTPMCMD enmCmd, const void *pvCmd, size_t cbCmd, uint32_t *pu32Resp,
337 void *pvResp, size_t cbResp, RTMSINTERVAL cMillies)
338{
339 SWTPMHDR Hdr;
340 RTSGBUF SgBuf;
341 RTSGSEG aSegs[2];
342 uint32_t cSegs = 1;
343
344 Hdr.enmCmd = (SWTPMCMD)RT_H2BE_U32(enmCmd);
345 aSegs[0].pvSeg = &Hdr;
346 aSegs[0].cbSeg = sizeof(Hdr);
347 if (cbCmd)
348 {
349 cSegs++;
350 aSegs[1].pvSeg = (void *)pvCmd;
351 aSegs[1].cbSeg = cbCmd;
352 }
353
354 RTSgBufInit(&SgBuf, &aSegs[0], cSegs);
355 int rc = RTSocketSgWrite(pThis->hSockCtrl, &SgBuf);
356 if (RT_SUCCESS(rc))
357 {
358 rc = RTSocketSelectOne(pThis->hSockCtrl, cMillies);
359 if (RT_SUCCESS(rc))
360 {
361 uint32_t u32Resp = 0;
362 rc = RTSocketRead(pThis->hSockCtrl, &u32Resp, sizeof(u32Resp), NULL /*pcbRead*/);
363 if (RT_SUCCESS(rc))
364 {
365 *pu32Resp = RT_BE2H_U32(u32Resp);
366 if (*pu32Resp == 0)
367 {
368 if (cbResp)
369 rc = RTSocketRead(pThis->hSockCtrl, pvResp, cbResp, NULL /*pcbRead*/);
370 }
371 else
372 rc = VERR_NET_IO_ERROR;
373 }
374 }
375 }
376
377 return rc;
378}
379
380
381/**
382 * Continue receiving a response from a previous call of drvTpmEmuExecCtrlCmdEx() or
383 * drvTpmEmuExecCtrlCmdNoPayload().
384 *
385 * @param pThis Pointer to the TPM emulator driver instance data.
386 * @param enmCmd The command to execute.
387 * @param pvResp Where to store additional resposne data.
388 * @param cbResp Size of the additional response data in bytes.
389 * @param cMillies Number of milliseconds to wait before aborting the receive with a timeout error.
390 */
391static int drvTpmEmuExecCtrlCmdRespCont(PDRVTPMEMU pThis, void *pvResp, size_t cbResp, RTMSINTERVAL cMillies)
392{
393 int rc = RTSocketSelectOne(pThis->hSockCtrl, cMillies);
394 if (RT_SUCCESS(rc))
395 rc = RTSocketRead(pThis->hSockCtrl, pvResp, cbResp, NULL /*pcbRead*/);
396
397 return rc;
398}
399
400
401/**
402 * Executes the given command over the control connection to the TPM emulator - variant with no command payload.
403 *
404 * @returns VBox status code.
405 * @retval VERR_NET_IO_ERROR if the executed command returned an error in the response status field.
406 * @param pThis Pointer to the TPM emulator driver instance data.
407 * @param enmCmd The command to execute.
408 * @param pvResp Where to store additional resposne data.
409 * @param cbResp Size of the Response data in bytes (excluding the response status code which is implicit).
410 * @param cMillies Number of milliseconds to wait before aborting the command with a timeout error.
411 */
412static int drvTpmEmuExecCtrlCmdNoPayload(PDRVTPMEMU pThis, SWTPMCMD enmCmd, void *pvResp, size_t cbResp, RTMSINTERVAL cMillies)
413{
414 uint32_t u32Resp = 0;
415 int rc = drvTpmEmuExecCtrlCmdEx(pThis, enmCmd, NULL /*pvCmd*/, 0 /*cbCmd*/, &u32Resp,
416 pvResp, cbResp, cMillies);
417 if (RT_SUCCESS(rc))
418 {
419 if (u32Resp != 0)
420 rc = VERR_NET_IO_ERROR;
421 }
422
423 return rc;
424}
425
426
427/**
428 * Executes the given command over the control connection to the TPM emulator - variant with no response payload other than the result.
429 *
430 * @returns VBox status code.
431 * @retval VERR_NET_IO_ERROR if the executed command returned an error in the response status field.
432 * @param pThis Pointer to the TPM emulator driver instance data.
433 * @param enmCmd The command to execute.
434 * @param pvCmd Additional command data to send.
435 * @param cbCmd Size of the additional command data in bytes.
436 * @param pu32Resp Where to store the response code from the reply.
437 * @param cMillies Number of milliseconds to wait before aborting the command with a timeout error.
438 */
439static int drvTpmEmuExecCtrlCmdNoResp(PDRVTPMEMU pThis, SWTPMCMD enmCmd, const void *pvCmd, size_t cbCmd, uint32_t *pu32Resp,
440 RTMSINTERVAL cMillies)
441{
442 return drvTpmEmuExecCtrlCmdEx(pThis, enmCmd, pvCmd, cbCmd, pu32Resp,
443 NULL /*pvResp*/, 0 /*cbResp*/, cMillies);
444}
445
446
447/**
448 * Executes the given command over the control connection to the TPM emulator - variant with no command and response payload.
449 *
450 * @returns VBox status code.
451 * @retval VERR_NET_IO_ERROR if the executed command returned an error in the response status field.
452 * @param pThis Pointer to the TPM emulator driver instance data.
453 * @param enmCmd The command to execute.
454 * @param cMillies Number of milliseconds to wait before aborting the command with a timeout error.
455 */
456static int drvTpmEmuExecCtrlCmdNoPayloadAndResp(PDRVTPMEMU pThis, SWTPMCMD enmCmd, RTMSINTERVAL cMillies)
457{
458 uint32_t u32Resp = 0;
459 int rc = drvTpmEmuExecCtrlCmdEx(pThis, enmCmd, NULL /*pvCmd*/, 0 /*cbCmd*/, &u32Resp,
460 NULL /*pvResp*/, 0 /*cbResp*/, cMillies);
461 if (RT_SUCCESS(rc))
462 {
463 if (u32Resp != 0)
464 rc = VERR_NET_IO_ERROR;
465 }
466
467 return rc;
468}
469
470
471/**
472 * Queries the version of the TPM offered by the remote emulator.
473 *
474 * @returns VBox status code.
475 * @param pThis Pointer to the TPM emulator driver instance data.
476 */
477static int drvTpmEmuQueryTpmVersion(PDRVTPMEMU pThis)
478{
479 SWTPMCMDGETCONFIG Cmd;
480 SWTPMRESPGETCONFIG Resp;
481 uint8_t abData[_4K];
482 uint32_t u32Resp = 0;
483
484 RT_ZERO(Cmd); RT_ZERO(Resp);
485 Cmd.u64Flags = RT_H2BE_U64(SWTPM_GET_CONFIG_F_TPM_SPECIFICATION);
486 Cmd.u32Offset = 0;
487 int rc = drvTpmEmuExecCtrlCmdEx(pThis, SWTPMCMD_GET_INFO, &Cmd, sizeof(Cmd), &u32Resp,
488 &Resp, sizeof(Resp), RT_MS_10SEC);
489 if (RT_SUCCESS(rc))
490 {
491 /*
492 * Currently it is not necessary to get the information in chunks, a single
493 * transaction is enough. To fend off future versions of swtpm requiring this
494 * we return an error here if the total length is not equal to the length of the chunk.
495 */
496 if (RT_BE2H_U32(Resp.cbTotal) == RT_BE2H_U32(Resp.cbThis))
497 {
498 /* Fetch the response body. */
499 rc = drvTpmEmuExecCtrlCmdRespCont(pThis, &abData[0], RT_BE2H_U32(Resp.cbThis), RT_MS_10SEC);
500 if (RT_SUCCESS(rc))
501 {
502 RTJSONVAL hJsonVal = NIL_RTJSONVAL;
503 rc = RTJsonParseFromBuf(&hJsonVal, &abData[0], RT_BE2H_U32(Resp.cbThis), NULL /*pErrInfo*/);
504 if (RT_SUCCESS(rc))
505 {
506 RTJSONVAL hJsonTpmSpec = NIL_RTJSONVAL;
507 rc = RTJsonValueQueryByName(hJsonVal, "TPMSpecification", &hJsonTpmSpec);
508 if (RT_SUCCESS(rc))
509 {
510 RTJSONVAL hJsonTpmFam = NIL_RTJSONVAL;
511 rc = RTJsonValueQueryByName(hJsonTpmSpec, "family", &hJsonTpmFam);
512 if (RT_SUCCESS(rc))
513 {
514 const char *pszFam = NULL;
515 rc = RTJsonValueQueryString(hJsonTpmFam, &pszFam);
516 if (RT_SUCCESS(rc))
517 {
518 if (!RTStrCmp(pszFam, "1.2"))
519 pThis->enmTpmVers = TPMVERSION_1_2;
520 else if (!RTStrCmp(pszFam, "2.0"))
521 pThis->enmTpmVers = TPMVERSION_2_0;
522 else
523 pThis->enmTpmVers = TPMVERSION_UNKNOWN;
524 }
525
526 RTJsonValueRelease(hJsonTpmFam);
527 }
528
529 RTJsonValueRelease(hJsonTpmSpec);
530 }
531
532 RTJsonValueRelease(hJsonVal);
533 }
534 }
535 }
536 else
537 rc = VERR_NOT_SUPPORTED;
538 }
539
540 return rc;
541}
542
543
544/**
545 * Queries the capabilities of the remote TPM emulator and verifies that
546 * it offers everything we require for operation.
547 *
548 * @returns VBox status code.
549 * @param pThis Pointer to the TPM emulator driver instance data.
550 */
551static int drvTpmEmuQueryCaps(PDRVTPMEMU pThis)
552{
553 SWTPMRESPGETCAPABILITY Resp;
554 int rc = drvTpmEmuExecCtrlCmdNoPayload(pThis, SWTPMCMD_GET_CAPABILITY, &Resp, sizeof(Resp), RT_MS_10SEC);
555 if (RT_SUCCESS(rc))
556 pThis->fCaps = RT_BE2H_U32(Resp.u32Caps);
557
558 return rc;
559}
560
561
562/**
563 * Queries the maximum supported buffer size by the emulation.
564 *
565 * @returns VBox status code.
566 * @param pThis Pointer to the TPM emulator driver instance data.
567 * @param pcbBufferMax Where to store the maximum supported buffer size on success.
568 */
569static int drvTpmEmuQueryBufferSzMax(PDRVTPMEMU pThis, uint32_t *pcbBufferMax)
570{
571 SWTPMCMDSETBUFSZ Cmd;
572 SWTPMRESPSETBUFSZ Resp;
573 uint32_t u32Resp = 0;
574
575 RT_ZERO(Cmd); RT_ZERO(Resp);
576 Cmd.cbBuffer = RT_H2BE_U32(0);
577 int rc = drvTpmEmuExecCtrlCmdEx(pThis, SWTPMCMD_SET_BUFFERSIZE, &Cmd, sizeof(Cmd), &u32Resp,
578 &Resp, sizeof(Resp), RT_MS_10SEC);
579 if (RT_SUCCESS(rc))
580 {
581 if (u32Resp == 0)
582 *pcbBufferMax = RT_BE2H_U32(Resp.cbBufferMax);
583 else
584 rc = VERR_NET_IO_ERROR;
585 }
586
587 return rc;
588}
589
590
591/**
592 * Queries the maximum supported buffer size by the emulation.
593 *
594 * @returns VBox status code.
595 * @param pThis Pointer to the TPM emulator driver instance data.
596 * @param cbBuffer The buffer size to set.
597 */
598static int drvTpmEmuSetBufferSz(PDRVTPMEMU pThis, uint32_t cbBuffer)
599{
600 SWTPMCMDSETBUFSZ Cmd;
601 SWTPMRESPSETBUFSZ Resp;
602 uint32_t u32Resp = 0;
603
604 RT_ZERO(Cmd); RT_ZERO(Resp);
605 Cmd.cbBuffer = RT_H2BE_U32(cbBuffer);
606 int rc = drvTpmEmuExecCtrlCmdEx(pThis, SWTPMCMD_SET_BUFFERSIZE, &Cmd, sizeof(Cmd), &u32Resp,
607 &Resp, sizeof(Resp), RT_MS_10SEC);
608 if ( RT_SUCCESS(rc)
609 && u32Resp != 0)
610 rc = VERR_NET_IO_ERROR;
611
612 return rc;
613}
614
615
616/**
617 * Sets the given locality for the emulated TPM.
618 *
619 * @returns VBox status code.
620 * @param pThis Pointer to the TPM emulator driver instance data.
621 * @param bLoc The locality to set.
622 */
623static int drvTpmEmuSetLocality(PDRVTPMEMU pThis, uint8_t bLoc)
624{
625 SWTPMCMDSETLOCALITY Cmd;
626 uint32_t u32Resp = 0;
627
628 Cmd.bLoc = bLoc;
629 int rc = drvTpmEmuExecCtrlCmdNoResp(pThis, SWTPMCMD_SET_LOCALITY, &Cmd, sizeof(Cmd), &u32Resp, RT_MS_10SEC);
630 if ( RT_SUCCESS(rc)
631 && u32Resp != 0)
632 rc = VERR_NET_IO_ERROR;
633
634 if (RT_SUCCESS(rc))
635 pThis->bLoc = bLoc;
636
637 return rc;
638}
639
640
641/** @interface_method_impl{PDMITPMCONNECTOR,pfnGetVersion} */
642static DECLCALLBACK(TPMVERSION) drvTpmEmuGetVersion(PPDMITPMCONNECTOR pInterface)
643{
644 PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
645 return pThis->enmTpmVers;
646}
647
648
649/** @interface_method_impl{PDMITPMCONNECTOR,pfnGetLocalityMax} */
650static DECLCALLBACK(uint32_t) drvTpmEmuGetLocalityMax(PPDMITPMCONNECTOR pInterface)
651{
652 RT_NOREF(pInterface);
653 return 4;
654}
655
656
657/** @interface_method_impl{PDMITPMCONNECTOR,pfnGetBufferSize} */
658static DECLCALLBACK(uint32_t) drvTpmEmuGetBufferSize(PPDMITPMCONNECTOR pInterface)
659{
660 PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
661 return pThis->cbBuffer;
662}
663
664
665/** @interface_method_impl{PDMITPMCONNECTOR,pfnGetEstablishedFlag} */
666static DECLCALLBACK(bool) drvTpmEmuGetEstablishedFlag(PPDMITPMCONNECTOR pInterface)
667{
668 PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
669
670 SWTPMRESPGETTPMEST Resp;
671 int rc = drvTpmEmuExecCtrlCmdNoPayload(pThis, SWTPMCMD_GET_TPMESTABLISHED, &Resp, sizeof(Resp), RT_MS_10SEC);
672 if (RT_SUCCESS(rc)
673 && Resp.fEst != 0)
674 return true;
675
676 return false;
677}
678
679
680/** @interface_method_impl{PDMITPMCONNECTOR,pfnResetEstablishedFlag} */
681static DECLCALLBACK(int) drvTpmEmuResetEstablishedFlag(PPDMITPMCONNECTOR pInterface, uint8_t bLoc)
682{
683 PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
684
685 SWTPMCMDRSTEST Cmd;
686 uint32_t u32Resp = 0;
687
688 Cmd.bLoc = bLoc;
689 int rc = drvTpmEmuExecCtrlCmdNoResp(pThis, SWTPMCMD_RESET_TPMESTABLISHED, &Cmd, sizeof(Cmd), &u32Resp, RT_MS_10SEC);
690 if ( RT_SUCCESS(rc)
691 && u32Resp != 0)
692 rc = VERR_NET_IO_ERROR;
693
694 return rc;
695}
696
697
698/** @interface_method_impl{PDMITPMCONNECTOR,pfnCmdExec} */
699static DECLCALLBACK(int) drvTpmEmuCmdExec(PPDMITPMCONNECTOR pInterface, uint8_t bLoc, const void *pvCmd, size_t cbCmd, void *pvResp, size_t cbResp)
700{
701 PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
702
703 int rc = VINF_SUCCESS;
704 if (pThis->bLoc != bLoc)
705 rc = drvTpmEmuSetLocality(pThis, bLoc);
706
707 if (RT_SUCCESS(rc))
708 {
709 rc = RTSocketWrite(pThis->hSockData, pvCmd, cbCmd);
710 if (RT_SUCCESS(rc))
711 {
712 rc = RTSocketSelectOne(pThis->hSockData, RT_MS_10SEC);
713 if (RT_SUCCESS(rc))
714 {
715 /* Read the response header in first. */
716 TPMRESPHDR RespHdr;
717 rc = RTSocketRead(pThis->hSockData, &RespHdr, sizeof(RespHdr), NULL /*pcbRead*/);
718 if (RT_SUCCESS(rc))
719 {
720 size_t cbHdrResp = RTTpmRespGetSz(&RespHdr);
721 if (cbHdrResp <= cbResp - sizeof(RespHdr))
722 {
723 memcpy(pvResp, &RespHdr, sizeof(RespHdr));
724
725 if (cbHdrResp > sizeof(RespHdr))
726 rc = RTSocketRead(pThis->hSockData, (uint8_t *)pvResp + sizeof(RespHdr), cbHdrResp - sizeof(RespHdr),
727 NULL /*pcbRead*/);
728 }
729 else
730 rc = VERR_BUFFER_OVERFLOW;
731 }
732 }
733 }
734 }
735
736 return rc;
737}
738
739
740/** @interface_method_impl{PDMITPMCONNECTOR,pfnCmdCancel} */
741static DECLCALLBACK(int) drvTpmEmuCmdCancel(PPDMITPMCONNECTOR pInterface)
742{
743 PDRVTPMEMU pThis = RT_FROM_MEMBER(pInterface, DRVTPMEMU, ITpmConnector);
744
745 return drvTpmEmuExecCtrlCmdNoPayloadAndResp(pThis, SWTPMCMD_CANCEL_TPM_CMD, RT_MS_10SEC);
746}
747
748
749/** @interface_method_impl{PDMIBASE,pfnQueryInterface} */
750static DECLCALLBACK(void *) drvTpmEmuQueryInterface(PPDMIBASE pInterface, const char *pszIID)
751{
752 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
753 PDRVTPMEMU pThis = PDMINS_2_DATA(pDrvIns, PDRVTPMEMU);
754 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
755 PDMIBASE_RETURN_INTERFACE(pszIID, PDMITPMCONNECTOR, &pThis->ITpmConnector);
756 return NULL;
757}
758
759
760/* -=-=-=-=- PDMDRVREG -=-=-=-=- */
761
762/**
763 * @interface_method_impl{PDMDRVREG,pfnPowerOn}
764 */
765static DECLCALLBACK(void) drvTpmEmuPowerOn(PPDMDRVINS pDrvIns)
766{
767 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
768 PDRVTPMEMU pThis = PDMINS_2_DATA(pDrvIns, PDRVTPMEMU);
769
770 SWTPMCMDTPMINIT Cmd;
771 uint32_t u32Resp = 0;
772
773 RT_ZERO(Cmd);
774 Cmd.u32Flags = 0;
775 int rc = drvTpmEmuExecCtrlCmdEx(pThis, SWTPMCMD_INIT, &Cmd, sizeof(Cmd), &u32Resp,
776 NULL, 0, RT_MS_10SEC);
777 if (RT_FAILURE(rc))
778 PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, "Failed to startup the TPM with %Rrc", rc);
779}
780
781
782/**
783 * @interface_method_impl{PDMDRVREG,pfnPowerOff}
784 */
785static DECLCALLBACK(void) drvTpmEmuPowerOff(PPDMDRVINS pDrvIns)
786{
787 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
788 PDRVTPMEMU pThis = PDMINS_2_DATA(pDrvIns, PDRVTPMEMU);
789
790 int rc = drvTpmEmuExecCtrlCmdNoPayload(pThis, SWTPMCMD_SHUTDOWN, NULL, 0, RT_MS_10SEC);
791 if (RT_FAILURE(rc))
792 PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, "Failed to shutdown the TPM with %Rrc", rc);
793}
794
795
796/** @copydoc FNPDMDRVDESTRUCT */
797static DECLCALLBACK(void) drvTpmEmuDestruct(PPDMDRVINS pDrvIns)
798{
799 PDRVTPMEMU pThis = PDMINS_2_DATA(pDrvIns, PDRVTPMEMU);
800 LogFlow(("%s\n", __FUNCTION__));
801 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
802
803 if (pThis->hSockCtrl != NIL_RTSOCKET)
804 {
805 int rc = RTSocketShutdown(pThis->hSockCtrl, true /* fRead */, true /* fWrite */);
806 AssertRC(rc);
807
808 rc = RTSocketClose(pThis->hSockCtrl);
809 AssertRC(rc); RT_NOREF(rc);
810
811 pThis->hSockCtrl = NIL_RTSOCKET;
812 }
813
814 if (pThis->hSockData != NIL_RTSOCKET)
815 {
816 int rc = RTSocketShutdown(pThis->hSockData, true /* fRead */, true /* fWrite */);
817 AssertRC(rc);
818
819 rc = RTSocketClose(pThis->hSockData);
820 AssertRC(rc); RT_NOREF(rc);
821
822 pThis->hSockCtrl = NIL_RTSOCKET;
823 }
824}
825
826
827/** @copydoc FNPDMDRVCONSTRUCT */
828static DECLCALLBACK(int) drvTpmEmuConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
829{
830 RT_NOREF(fFlags);
831 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
832 PDRVTPMEMU pThis = PDMINS_2_DATA(pDrvIns, PDRVTPMEMU);
833 PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
834
835 /*
836 * Init the static parts.
837 */
838 pThis->pDrvIns = pDrvIns;
839 pThis->hSockCtrl = NIL_RTSOCKET;
840 pThis->hSockData = NIL_RTSOCKET;
841 pThis->enmTpmVers = TPMVERSION_UNKNOWN;
842 pThis->bLoc = TPM_NO_LOCALITY_SELECTED;
843
844 /* IBase */
845 pDrvIns->IBase.pfnQueryInterface = drvTpmEmuQueryInterface;
846 /* ITpmConnector */
847 pThis->ITpmConnector.pfnGetVersion = drvTpmEmuGetVersion;
848 pThis->ITpmConnector.pfnGetLocalityMax = drvTpmEmuGetLocalityMax;
849 pThis->ITpmConnector.pfnGetBufferSize = drvTpmEmuGetBufferSize;
850 pThis->ITpmConnector.pfnGetEstablishedFlag = drvTpmEmuGetEstablishedFlag;
851 pThis->ITpmConnector.pfnResetEstablishedFlag = drvTpmEmuResetEstablishedFlag;
852 pThis->ITpmConnector.pfnCmdExec = drvTpmEmuCmdExec;
853 pThis->ITpmConnector.pfnCmdCancel = drvTpmEmuCmdCancel;
854
855 /*
856 * Validate and read the configuration.
857 */
858 PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "Location|BufferSize", "");
859
860 char szLocation[_1K];
861 int rc = pHlp->pfnCFGMQueryString(pCfg, "Location", &szLocation[0], sizeof(szLocation));
862 if (RT_FAILURE(rc))
863 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
864 N_("Configuration error: querying \"Location\" resulted in %Rrc"), rc);
865
866 /*
867 * Create/Open the socket.
868 */
869 char *pszPort = strchr(szLocation, ':');
870 if (!pszPort)
871 return PDMDrvHlpVMSetError(pDrvIns, VERR_NOT_FOUND, RT_SRC_POS,
872 N_("DrvTpmEmu#%d: The location misses the port to connect to"),
873 pDrvIns->iInstance);
874
875 *pszPort = '\0'; /* Overwrite temporarily to avoid copying the hostname into a temporary buffer. */
876 uint32_t uPort = 0;
877 rc = RTStrToUInt32Ex(pszPort + 1, NULL, 10, &uPort);
878 if (RT_FAILURE(rc))
879 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
880 N_("DrvTpmEmu#%d: The port part of the location is not a numerical value"),
881 pDrvIns->iInstance);
882
883 rc = RTTcpClientConnect(szLocation, uPort, &pThis->hSockCtrl);
884 *pszPort = ':'; /* Restore delimiter before checking the status. */
885 if (RT_FAILURE(rc))
886 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
887 N_("DrvTpmEmu#%d failed to connect to control socket %s"),
888 pDrvIns->iInstance, szLocation);
889
890 rc = drvTpmEmuQueryCaps(pThis);
891 if (RT_FAILURE(rc))
892 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
893 N_("DrvTpmEmu#%d failed to query capabilities offered by %s"),
894 pDrvIns->iInstance, szLocation);
895
896 if (!(pThis->fCaps & SWTPM_CAP_GET_CONFIG))
897 return PDMDrvHlpVMSetError(pDrvIns, VERR_NOT_SUPPORTED, RT_SRC_POS,
898 N_("DrvTpmEmu#%d Emulated TPM at '%s' misses the GET_CONFIG capability"),
899 pDrvIns->iInstance, szLocation);
900
901 rc = drvTpmEmuQueryTpmVersion(pThis);
902 if (RT_FAILURE(rc))
903 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
904 N_("DrvTpmEmu#%d failed to query TPM version from %s"),
905 pDrvIns->iInstance, szLocation);
906
907 if (pThis->enmTpmVers == TPMVERSION_UNKNOWN)
908 return PDMDrvHlpVMSetError(pDrvIns, VERR_NOT_SUPPORTED, RT_SRC_POS,
909 N_("DrvTpmEmu#%d Emulated TPM version of %s is not supported"),
910 pDrvIns->iInstance, szLocation);
911
912 const char *pszTpmVers = NULL;
913 uint32_t fCapsReq = SWTPM_CAP_INIT | SWTPM_CAP_SHUTDOWN | SWTPM_CAP_GET_TPMESTABLISHED
914 | SWTPM_CAP_SET_LOCALITY | SWTPM_CAP_CANCEL_TPM_CMD | SWTPM_CAP_GET_STATEBLOB
915 | SWTPM_CAP_SET_STATEBLOB | SWTPM_CAP_STOP | SWTPM_CAP_SET_BUFFERSIZE;
916 switch (pThis->enmTpmVers)
917 {
918 case TPMVERSION_1_2:
919 /* No additional capabilities needed. */
920 pszTpmVers = "1.2";
921 break;
922 case TPMVERSION_2_0:
923 fCapsReq |= SWTPM_CAP_RESET_TPMESTABLISHED;
924 pszTpmVers = "2.0";
925 break;
926 default:
927 AssertMsgFailedReturn(("DrvTpmEmu#%d Emulated TPM version %d is not correctly handled", pDrvIns->iInstance, pThis->enmTpmVers),
928 VERR_INVALID_STATE);
929 }
930
931 if ((pThis->fCaps & fCapsReq) != fCapsReq)
932 return PDMDrvHlpVMSetError(pDrvIns, VERR_NOT_SUPPORTED, RT_SRC_POS,
933 N_("DrvTpmEmu#%d Emulated TPM version of %s does not offer required set of capabilities (%#x requested vs. %#x offered)"),
934 pDrvIns->iInstance, szLocation, fCapsReq, pThis->fCaps);
935
936 uint32_t cbBufferMax = 0;
937 rc = drvTpmEmuQueryBufferSzMax(pThis, &cbBufferMax);
938 if (RT_FAILURE(rc))
939 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
940 N_("DrvTpmEmu#%d failed to query maximum buffer size from %s"),
941 pDrvIns->iInstance, szLocation);
942
943 /* Configure the buffer size. */
944 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "BufferSize", &pThis->cbBuffer, cbBufferMax);
945 if (RT_FAILURE(rc))
946 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
947 N_("Configuration error: querying \"BufferSize\" resulted in %Rrc"), rc);
948
949 /* Set the buffer size. */
950 rc = drvTpmEmuSetBufferSz(pThis, pThis->cbBuffer);
951 if (RT_FAILURE(rc))
952 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
953 N_("DrvTpmEmu#%d failed to set buffer size to %u for %s"),
954 pDrvIns->iInstance, pThis->cbBuffer, szLocation);
955
956 /* Connect the data channel now. */
957 /** @todo Allow configuring a different port. */
958 *pszPort = '\0'; /* Overwrite temporarily to avoid copying the hostname into a temporary buffer. */
959 rc = RTTcpClientConnect(szLocation, uPort + 1, &pThis->hSockData);
960 *pszPort = ':'; /* Restore delimiter before checking the status. */
961 if (RT_FAILURE(rc))
962 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
963 N_("DrvTpmEmu#%d failed to connect to data socket %s"),
964 pDrvIns->iInstance, szLocation);
965
966 LogRel(("DrvTpmEmu#%d: Connected to %s, emulating TPM version %s\n", pDrvIns->iInstance, szLocation, pszTpmVers));
967 return VINF_SUCCESS;
968}
969
970
971/**
972 * TPM emulator driver registration record.
973 */
974const PDMDRVREG g_DrvTpmEmu =
975{
976 /* u32Version */
977 PDM_DRVREG_VERSION,
978 /* szName */
979 "TpmEmu",
980 /* szRCMod */
981 "",
982 /* szR0Mod */
983 "",
984 /* pszDescription */
985 "TPM emulator driver.",
986 /* fFlags */
987 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
988 /* fClass. */
989 PDM_DRVREG_CLASS_STREAM,
990 /* cMaxInstances */
991 ~0U,
992 /* cbInstance */
993 sizeof(DRVTPMEMU),
994 /* pfnConstruct */
995 drvTpmEmuConstruct,
996 /* pfnDestruct */
997 drvTpmEmuDestruct,
998 /* pfnRelocate */
999 NULL,
1000 /* pfnIOCtl */
1001 NULL,
1002 /* pfnPowerOn */
1003 drvTpmEmuPowerOn,
1004 /* pfnReset */
1005 NULL,
1006 /* pfnSuspend */
1007 NULL,
1008 /* pfnResume */
1009 NULL,
1010 /* pfnAttach */
1011 NULL,
1012 /* pfnDetach */
1013 NULL,
1014 /* pfnPowerOff */
1015 drvTpmEmuPowerOff,
1016 /* pfnSoftReset */
1017 NULL,
1018 /* u32EndVersion */
1019 PDM_DRVREG_VERSION
1020};
1021
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use