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 response_size = 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     /*
61 	 * Call tcti_receive with NULL response buffer to get the actual size
62 	 * of the response. If we can read the response in multiple chunks
63 	 * then the tcti should read the response header first and give us
64 	 * the acctual size. If not it should set the response size to the
65 	 * maximum possible size.
66      */
67     rval = Tss2_Tcti_Receive(ctx->tctiContext, &response_size,
68                              NULL, timeout);
69     if (rval)
70         return rval;
71 
72     if (response_size < sizeof(TPM20_Header_Out)) {
73         ctx->previousStage = CMD_STAGE_PREPARE;
74         return TSS2_SYS_RC_INSUFFICIENT_RESPONSE;
75     }
76     if (response_size > ctx->maxCmdSize) {
77         ctx->previousStage = CMD_STAGE_PREPARE;
78         LOG_ERROR("Response size to big: %zu > %u", response_size, ctx->maxCmdSize);
79         return TSS2_SYS_RC_INSUFFICIENT_CONTEXT;
80     }
81 
82     /* Then call receive again with the response buffer to read the response */
83     rval = Tss2_Tcti_Receive(ctx->tctiContext, &response_size,
84                              ctx->cmdBuffer, timeout);
85     if (rval == TSS2_TCTI_RC_INSUFFICIENT_BUFFER) {
86         LOG_ERROR("TCTI: Insufficient Buffer.");
87         return TSS2_SYS_RC_INSUFFICIENT_CONTEXT;
88     }
89 
90     if (rval)
91         return rval;
92 
93     /*
94      * Unmarshal the tag, response size, and response code as soon
95      * as possible. Later processing code should get this data from
96      * the TPM20_Header_Out in the context structure. No need to
97      * unmarshal this stuff again.
98      */
99     ctx->nextData = 0;
100 
101     rval = Tss2_MU_TPM2_ST_Unmarshal(ctx->cmdBuffer,
102                                      ctx->maxCmdSize,
103                                      &ctx->nextData,
104                                      &ctx->rsp_header.tag);
105     if (rval) {
106         LOG_ERROR("Unmarshaling response tag. RC=%" PRIx32, rval);
107         return rval;
108     }
109 
110     if (ctx->rsp_header.tag != TPM2_ST_SESSIONS &&
111         ctx->rsp_header.tag != TPM2_ST_NO_SESSIONS) {
112         if (ctx->rsp_header.tag == TPM2_ST_RSP_COMMAND) {
113             LOG_ERROR("Unsupported device. The device is a TPM 1.2");
114             return TSS2_SYS_RC_GENERAL_FAILURE;
115         } else {
116             LOG_ERROR("Malformed reponse: Invalid tag in response header: %" PRIx16,
117                       ctx->rsp_header.tag);
118             return TSS2_SYS_RC_MALFORMED_RESPONSE;
119         }
120     }
121 
122     rval = Tss2_MU_UINT32_Unmarshal(ctx->cmdBuffer,
123                                      ctx->maxCmdSize,
124                                      &ctx->nextData,
125                                      &ctx->rsp_header.responseSize);
126     if (rval)
127         return rval;
128 
129     if (ctx->rsp_header.responseSize > ctx->maxCmdSize) {
130         return TSS2_SYS_RC_MALFORMED_RESPONSE;
131     }
132 
133     rval = Tss2_MU_UINT32_Unmarshal(ctx->cmdBuffer,
134                                     ctx->maxCmdSize,
135                                     &ctx->nextData,
136                                     &ctx->rsp_header.responseCode);
137     if (rval)
138         return rval;
139 
140     rval = ctx->rsp_header.responseCode;
141 
142     /* If didn't receive enough response bytes, reset SAPI state machine to
143      * CMD_STAGE_PREPARE. There's nothing else we can do for current command.
144      */
145     if (ctx->rsp_header.responseSize < sizeof(TPM20_Header_Out)) {
146         ctx->previousStage = CMD_STAGE_PREPARE;
147         return TSS2_SYS_RC_INSUFFICIENT_RESPONSE;
148     }
149 
150     /* If we received a TPM error then reset SAPI state machine to
151      * CMD_STAGE_PREPARE, and restore the command header so the command
152      * can be reissued without going through the usual *_prepare stage.
153      */
154     if (rval && rval != TPM2_RC_INITIALIZE) {
155         ctx->previousStage = CMD_STAGE_PREPARE;
156         memcpy(ctx->cmdBuffer, ctx->cmd_header, sizeof(ctx->cmd_header));
157         return rval;
158     }
159 
160     ctx->previousStage = CMD_STAGE_RECEIVE_RESPONSE;
161     return rval;
162 }
163 
Tss2_Sys_Execute(TSS2_SYS_CONTEXT * sysContext)164 TSS2_RC Tss2_Sys_Execute(TSS2_SYS_CONTEXT *sysContext)
165 {
166     TSS2_RC rval;
167 
168     if (!sysContext)
169         return TSS2_SYS_RC_BAD_REFERENCE;
170 
171     rval = Tss2_Sys_ExecuteAsync(sysContext);
172     if (rval)
173         return rval;
174 
175     return Tss2_Sys_ExecuteFinish(sysContext, TSS2_TCTI_TIMEOUT_BLOCK);
176 }
177