1 /* SPDX-License-Identifier: BSD-2-Clause */
2 /***********************************************************************;
3  * Copyright (c) 2015 - 2018, Intel Corporation
4  * All rights reserved.
5  ***********************************************************************/
6 
7 #ifdef HAVE_CONFIG_H
8 #include <config.h>
9 #endif
10 
11 #include <inttypes.h>
12 #include <string.h>
13 
14 #include "tss2_tpm2_types.h"
15 #include "tss2_mu.h"
16 #include "sysapi_util.h"
17 #include "util/tss2_endian.h"
18 #define LOGMODULE sys
19 #include "util/log.h"
20 
Tss2_Sys_ExecuteAsync(TSS2_SYS_CONTEXT * sysContext)21 TSS2_RC Tss2_Sys_ExecuteAsync(TSS2_SYS_CONTEXT *sysContext)
22 {
23     _TSS2_SYS_CONTEXT_BLOB *ctx = syscontext_cast(sysContext);
24     TSS2_RC rval;
25 
26     if (!ctx)
27         return TSS2_SYS_RC_BAD_REFERENCE;
28 
29     if (ctx->previousStage != CMD_STAGE_PREPARE)
30         return TSS2_SYS_RC_BAD_SEQUENCE;
31 
32     rval = Tss2_Tcti_Transmit(ctx->tctiContext,
33                               HOST_TO_BE_32(req_header_from_cxt(ctx)->commandSize),
34                               ctx->cmdBuffer);
35     if (rval)
36         return rval;
37 
38     /* Keep a copy of the cmd header to be able reissue the command
39      * after receiving a TPM error
40      */
41     memcpy(ctx->cmd_header, ctx->cmdBuffer, sizeof(ctx->cmd_header));
42 
43     ctx->previousStage = CMD_STAGE_SEND_COMMAND;
44 
45     return rval;
46 }
47 
Tss2_Sys_ExecuteFinish(TSS2_SYS_CONTEXT * sysContext,int32_t timeout)48 TSS2_RC Tss2_Sys_ExecuteFinish(TSS2_SYS_CONTEXT *sysContext, int32_t timeout)
49 {
50     _TSS2_SYS_CONTEXT_BLOB *ctx = syscontext_cast(sysContext);
51     TSS2_RC rval;
52     size_t responseSize = 0;
53 
54     if (!ctx)
55         return TSS2_SYS_RC_BAD_REFERENCE;
56 
57     if (ctx->previousStage != CMD_STAGE_SEND_COMMAND)
58         return TSS2_SYS_RC_BAD_SEQUENCE;
59 
60 #ifdef TCTI_PARTIAL_READ
61     /*
62      * First call receive with NULL as the response buffer to
63      * get the size of the response
64      */
65     rval = Tss2_Tcti_Receive(ctx->tctiContext, &responseSize,
66                              NULL, timeout);
67     if (rval)
68         return rval;
69 
70     if (responseSize < sizeof(TPM20_Header_Out)) {
71         ctx->previousStage = CMD_STAGE_PREPARE;
72         return TSS2_SYS_RC_INSUFFICIENT_RESPONSE;
73     }
74     if (responseSize > ctx->maxCmdSize) {
75         ctx->previousStage = CMD_STAGE_PREPARE;
76         return TSS2_SYS_RC_INSUFFICIENT_CONTEXT;
77     }
78 #else
79     /* For none partial reads set the size to maxCmdSize */
80     responseSize = ctx->maxCmdSize;
81 #endif
82 
83     /*
84      * Then call receive again with the response buffer to read the response
85      */
86     rval = Tss2_Tcti_Receive(ctx->tctiContext, &responseSize,
87                              ctx->cmdBuffer, timeout);
88     if (rval == TSS2_TCTI_RC_INSUFFICIENT_BUFFER)
89         return TSS2_SYS_RC_INSUFFICIENT_CONTEXT;
90 
91     if (rval)
92         return rval;
93 
94     /*
95      * Unmarshal the tag, response size, and response code as soon
96      * as possible. Later processing code should get this data from
97      * the TPM20_Header_Out in the context structure. No need to
98      * unmarshal this stuff again.
99      */
100     ctx->nextData = 0;
101 
102     rval = Tss2_MU_TPM2_ST_Unmarshal(ctx->cmdBuffer,
103                                      ctx->maxCmdSize,
104                                      &ctx->nextData,
105                                      &ctx->rsp_header.tag);
106     if (rval) {
107         LOG_ERROR("Unmarshaling response tag. RC=%" PRIx32, rval);
108         return rval;
109     }
110 
111     if (ctx->rsp_header.tag != TPM2_ST_SESSIONS &&
112         ctx->rsp_header.tag != TPM2_ST_NO_SESSIONS) {
113         if (ctx->rsp_header.tag == TPM2_ST_RSP_COMMAND) {
114             LOG_ERROR("Unsupported device. The device is a TPM 1.2");
115             return TSS2_SYS_RC_GENERAL_FAILURE;
116         } else {
117             LOG_ERROR("Malformed reponse: Invalid tag in response header: %" PRIx16,
118                       ctx->rsp_header.tag);
119             return TSS2_SYS_RC_MALFORMED_RESPONSE;
120         }
121     }
122 
123     rval = Tss2_MU_UINT32_Unmarshal(ctx->cmdBuffer,
124                                      ctx->maxCmdSize,
125                                      &ctx->nextData,
126                                      &ctx->rsp_header.responseSize);
127     if (rval)
128         return rval;
129 
130     if (ctx->rsp_header.responseSize > ctx->maxCmdSize) {
131         return TSS2_SYS_RC_MALFORMED_RESPONSE;
132     }
133 
134     rval = Tss2_MU_UINT32_Unmarshal(ctx->cmdBuffer,
135                                     ctx->maxCmdSize,
136                                     &ctx->nextData,
137                                     &ctx->rsp_header.responseCode);
138     if (rval)
139         return rval;
140 
141     rval = ctx->rsp_header.responseCode;
142 
143     /* If didn't receive enough response bytes, reset SAPI state machine to
144      * CMD_STAGE_PREPARE. There's nothing else we can do for current command.
145      */
146     if (ctx->rsp_header.responseSize < sizeof(TPM20_Header_Out)) {
147         ctx->previousStage = CMD_STAGE_PREPARE;
148         return TSS2_SYS_RC_INSUFFICIENT_RESPONSE;
149     }
150 
151     /* If we received a TPM error then reset SAPI state machine to
152      * CMD_STAGE_PREPARE, and restore the command header so the command
153      * can be reissued without going through the usual *_prepare stage.
154      */
155     if (rval && rval != TPM2_RC_INITIALIZE) {
156         ctx->previousStage = CMD_STAGE_PREPARE;
157         memcpy(ctx->cmdBuffer, ctx->cmd_header, sizeof(ctx->cmd_header));
158         return rval;
159     }
160 
161     ctx->previousStage = CMD_STAGE_RECEIVE_RESPONSE;
162     return rval;
163 }
164 
Tss2_Sys_Execute(TSS2_SYS_CONTEXT * sysContext)165 TSS2_RC Tss2_Sys_Execute(TSS2_SYS_CONTEXT *sysContext)
166 {
167     TSS2_RC rval;
168 
169     if (!sysContext)
170         return TSS2_SYS_RC_BAD_REFERENCE;
171 
172     rval = Tss2_Sys_ExecuteAsync(sysContext);
173     if (rval)
174         return rval;
175 
176     return Tss2_Sys_ExecuteFinish(sysContext, TSS2_TCTI_TIMEOUT_BLOCK);
177 }
178