• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* SPDX-License-Identifier: BSD-2-Clause */
2 /*******************************************************************************
3  * Copyright 2017-2018, Fraunhofer SIT sponsored by Infineon Technologies AG
4  * All rights reserved.
5  ******************************************************************************/
6 
7 #ifdef HAVE_CONFIG_H
8 #include <config.h>
9 #endif
10 
11 #include "tss2_mu.h"
12 #include "tss2_sys.h"
13 #include "tss2_esys.h"
14 
15 #include "esys_types.h"
16 #include "esys_iutil.h"
17 #include "esys_mu.h"
18 #define LOGMODULE esys
19 #include "util/log.h"
20 #include "util/aux_util.h"
21 
22 /** One-Call function for TPM2_FirmwareRead
23  *
24  * This function invokes the TPM2_FirmwareRead command in a one-call
25  * variant. This means the function will block until the TPM response is
26  * available. All input parameters are const. The memory for non-simple output
27  * parameters is allocated by the function implementation.
28  *
29  * @param[in,out] esysContext The ESYS_CONTEXT.
30  * @param[in]  shandle1 First session handle.
31  * @param[in]  shandle2 Second session handle.
32  * @param[in]  shandle3 Third session handle.
33  * @param[in]  sequenceNumber The number of previous calls to this command in
34  *             this sequence.
35  * @param[out] fuData Field upgrade image data.
36  *             (callee-allocated)
37  * @retval TSS2_RC_SUCCESS if the function call was a success.
38  * @retval TSS2_ESYS_RC_BAD_REFERENCE if the esysContext or required input
39  *         pointers or required output handle references are NULL.
40  * @retval TSS2_ESYS_RC_BAD_CONTEXT: if esysContext corruption is detected.
41  * @retval TSS2_ESYS_RC_MEMORY: if the ESAPI cannot allocate enough memory for
42  *         internal operations or return parameters.
43  * @retval TSS2_ESYS_RC_BAD_SEQUENCE: if the context has an asynchronous
44  *         operation already pending.
45  * @retval TSS2_ESYS_RC_INSUFFICIENT_RESPONSE: if the TPM's response does not
46  *          at least contain the tag, response length, and response code.
47  * @retval TSS2_ESYS_RC_MALFORMED_RESPONSE: if the TPM's response is corrupted.
48  * @retval TSS2_ESYS_RC_RSP_AUTH_FAILED: if the response HMAC from the TPM
49            did not verify.
50  * @retval TSS2_ESYS_RC_MULTIPLE_DECRYPT_SESSIONS: if more than one session has
51  *         the 'decrypt' attribute bit set.
52  * @retval TSS2_ESYS_RC_MULTIPLE_ENCRYPT_SESSIONS: if more than one session has
53  *         the 'encrypt' attribute bit set.
54  * @retval TSS2_ESYS_RC_NO_DECRYPT_PARAM: if one of the sessions has the
55  *         'decrypt' attribute set and the command does not support encryption
56  *         of the first command parameter.
57  * @retval TSS2_RCs produced by lower layers of the software stack may be
58  *         returned to the caller unaltered unless handled internally.
59  */
60 TSS2_RC
Esys_FirmwareRead(ESYS_CONTEXT * esysContext,ESYS_TR shandle1,ESYS_TR shandle2,ESYS_TR shandle3,UINT32 sequenceNumber,TPM2B_MAX_BUFFER ** fuData)61 Esys_FirmwareRead(
62     ESYS_CONTEXT *esysContext,
63     ESYS_TR shandle1,
64     ESYS_TR shandle2,
65     ESYS_TR shandle3,
66     UINT32 sequenceNumber,
67     TPM2B_MAX_BUFFER **fuData)
68 {
69     TSS2_RC r;
70 
71     r = Esys_FirmwareRead_Async(esysContext, shandle1, shandle2, shandle3,
72                                 sequenceNumber);
73     return_if_error(r, "Error in async function");
74 
75     /* Set the timeout to indefinite for now, since we want _Finish to block */
76     int32_t timeouttmp = esysContext->timeout;
77     esysContext->timeout = -1;
78     /*
79      * Now we call the finish function, until return code is not equal to
80      * from TSS2_BASE_RC_TRY_AGAIN.
81      * Note that the finish function may return TSS2_RC_TRY_AGAIN, even if we
82      * have set the timeout to -1. This occurs for example if the TPM requests
83      * a retransmission of the command via TPM2_RC_YIELDED.
84      */
85     do {
86         r = Esys_FirmwareRead_Finish(esysContext, fuData);
87         /* This is just debug information about the reattempt to finish the
88            command */
89         if ((r & ~TSS2_RC_LAYER_MASK) == TSS2_BASE_RC_TRY_AGAIN)
90             LOG_DEBUG("A layer below returned TRY_AGAIN: %" PRIx32
91                       " => resubmitting command", r);
92     } while ((r & ~TSS2_RC_LAYER_MASK) == TSS2_BASE_RC_TRY_AGAIN);
93 
94     /* Restore the timeout value to the original value */
95     esysContext->timeout = timeouttmp;
96     return_if_error(r, "Esys Finish");
97 
98     return TSS2_RC_SUCCESS;
99 }
100 
101 /** Asynchronous function for TPM2_FirmwareRead
102  *
103  * This function invokes the TPM2_FirmwareRead command in a asynchronous
104  * variant. This means the function will return as soon as the command has been
105  * sent downwards the stack to the TPM. All input parameters are const.
106  * In order to retrieve the TPM's response call Esys_FirmwareRead_Finish.
107  *
108  * @param[in,out] esysContext The ESYS_CONTEXT.
109  * @param[in]  shandle1 First session handle.
110  * @param[in]  shandle2 Second session handle.
111  * @param[in]  shandle3 Third session handle.
112  * @param[in]  sequenceNumber The number of previous calls to this command in
113  *             this sequence.
114  * @retval ESYS_RC_SUCCESS if the function call was a success.
115  * @retval TSS2_ESYS_RC_BAD_REFERENCE if the esysContext or required input
116  *         pointers or required output handle references are NULL.
117  * @retval TSS2_ESYS_RC_BAD_CONTEXT: if esysContext corruption is detected.
118  * @retval TSS2_ESYS_RC_MEMORY: if the ESAPI cannot allocate enough memory for
119  *         internal operations or return parameters.
120  * @retval TSS2_RCs produced by lower layers of the software stack may be
121            returned to the caller unaltered unless handled internally.
122  * @retval TSS2_ESYS_RC_MULTIPLE_DECRYPT_SESSIONS: if more than one session has
123  *         the 'decrypt' attribute bit set.
124  * @retval TSS2_ESYS_RC_MULTIPLE_ENCRYPT_SESSIONS: if more than one session has
125  *         the 'encrypt' attribute bit set.
126  * @retval TSS2_ESYS_RC_NO_DECRYPT_PARAM: if one of the sessions has the
127  *         'decrypt' attribute set and the command does not support encryption
128  *         of the first command parameter.
129  */
130 TSS2_RC
Esys_FirmwareRead_Async(ESYS_CONTEXT * esysContext,ESYS_TR shandle1,ESYS_TR shandle2,ESYS_TR shandle3,UINT32 sequenceNumber)131 Esys_FirmwareRead_Async(
132     ESYS_CONTEXT *esysContext,
133     ESYS_TR shandle1,
134     ESYS_TR shandle2,
135     ESYS_TR shandle3,
136     UINT32 sequenceNumber)
137 {
138     TSS2_RC r;
139     LOG_TRACE("context=%p, sequenceNumber=%"PRIx32 "",
140               esysContext, sequenceNumber);
141     TSS2L_SYS_AUTH_COMMAND auths;
142 
143     /* Check context, sequence correctness and set state to error for now */
144     if (esysContext == NULL) {
145         LOG_ERROR("esyscontext is NULL.");
146         return TSS2_ESYS_RC_BAD_REFERENCE;
147     }
148     r = iesys_check_sequence_async(esysContext);
149     if (r != TSS2_RC_SUCCESS)
150         return r;
151     esysContext->state = _ESYS_STATE_INTERNALERROR;
152 
153     /* Check input parameters */
154     r = check_session_feasibility(shandle1, shandle2, shandle3, 0);
155     return_state_if_error(r, _ESYS_STATE_INIT, "Check session usage");
156 
157     /* Initial invocation of SAPI to prepare the command buffer with parameters */
158     r = Tss2_Sys_FirmwareRead_Prepare(esysContext->sys, sequenceNumber);
159     return_state_if_error(r, _ESYS_STATE_INIT, "SAPI Prepare returned error.");
160 
161     /* Calculate the cpHash Values */
162     r = init_session_tab(esysContext, shandle1, shandle2, shandle3);
163     return_state_if_error(r, _ESYS_STATE_INIT, "Initialize session resources");
164     iesys_compute_session_value(esysContext->session_tab[0], NULL, NULL);
165     iesys_compute_session_value(esysContext->session_tab[1], NULL, NULL);
166     iesys_compute_session_value(esysContext->session_tab[2], NULL, NULL);
167 
168     /* Generate the auth values and set them in the SAPI command buffer */
169     r = iesys_gen_auths(esysContext, NULL, NULL, NULL, &auths);
170     return_state_if_error(r, _ESYS_STATE_INIT,
171                           "Error in computation of auth values");
172 
173     esysContext->authsCount = auths.count;
174     if (auths.count > 0) {
175         r = Tss2_Sys_SetCmdAuths(esysContext->sys, &auths);
176         return_state_if_error(r, _ESYS_STATE_INIT, "SAPI error on SetCmdAuths");
177     }
178 
179     /* Trigger execution and finish the async invocation */
180     r = Tss2_Sys_ExecuteAsync(esysContext->sys);
181     return_state_if_error(r, _ESYS_STATE_INTERNALERROR,
182                           "Finish (Execute Async)");
183 
184     esysContext->state = _ESYS_STATE_SENT;
185 
186     return r;
187 }
188 
189 /** Asynchronous finish function for TPM2_FirmwareRead
190  *
191  * This function returns the results of a TPM2_FirmwareRead command
192  * invoked via Esys_FirmwareRead_Finish. All non-simple output parameters
193  * are allocated by the function's implementation. NULL can be passed for every
194  * output parameter if the value is not required.
195  *
196  * @param[in,out] esysContext The ESYS_CONTEXT.
197  * @param[out] fuData Field upgrade image data.
198  *             (callee-allocated)
199  * @retval TSS2_RC_SUCCESS on success
200  * @retval ESYS_RC_SUCCESS if the function call was a success.
201  * @retval TSS2_ESYS_RC_BAD_REFERENCE if the esysContext or required input
202  *         pointers or required output handle references are NULL.
203  * @retval TSS2_ESYS_RC_BAD_CONTEXT: if esysContext corruption is detected.
204  * @retval TSS2_ESYS_RC_MEMORY: if the ESAPI cannot allocate enough memory for
205  *         internal operations or return parameters.
206  * @retval TSS2_ESYS_RC_BAD_SEQUENCE: if the context has an asynchronous
207  *         operation already pending.
208  * @retval TSS2_ESYS_RC_TRY_AGAIN: if the timeout counter expires before the
209  *         TPM response is received.
210  * @retval TSS2_ESYS_RC_INSUFFICIENT_RESPONSE: if the TPM's response does not
211  *         at least contain the tag, response length, and response code.
212  * @retval TSS2_ESYS_RC_RSP_AUTH_FAILED: if the response HMAC from the TPM did
213  *         not verify.
214  * @retval TSS2_ESYS_RC_MALFORMED_RESPONSE: if the TPM's response is corrupted.
215  * @retval TSS2_RCs produced by lower layers of the software stack may be
216  *         returned to the caller unaltered unless handled internally.
217  */
218 TSS2_RC
Esys_FirmwareRead_Finish(ESYS_CONTEXT * esysContext,TPM2B_MAX_BUFFER ** fuData)219 Esys_FirmwareRead_Finish(
220     ESYS_CONTEXT *esysContext,
221     TPM2B_MAX_BUFFER **fuData)
222 {
223     TSS2_RC r;
224     LOG_TRACE("context=%p, fuData=%p",
225               esysContext, fuData);
226 
227     if (esysContext == NULL) {
228         LOG_ERROR("esyscontext is NULL.");
229         return TSS2_ESYS_RC_BAD_REFERENCE;
230     }
231 
232     /* Check for correct sequence and set sequence to irregular for now */
233     if (esysContext->state != _ESYS_STATE_SENT &&
234         esysContext->state != _ESYS_STATE_RESUBMISSION) {
235         LOG_ERROR("Esys called in bad sequence.");
236         return TSS2_ESYS_RC_BAD_SEQUENCE;
237     }
238     esysContext->state = _ESYS_STATE_INTERNALERROR;
239 
240     /* Allocate memory for response parameters */
241     if (fuData != NULL) {
242         *fuData = calloc(sizeof(TPM2B_MAX_BUFFER), 1);
243         if (*fuData == NULL) {
244             return_error(TSS2_ESYS_RC_MEMORY, "Out of memory");
245         }
246     }
247 
248     /*Receive the TPM response and handle resubmissions if necessary. */
249     r = Tss2_Sys_ExecuteFinish(esysContext->sys, esysContext->timeout);
250     if ((r & ~TSS2_RC_LAYER_MASK) == TSS2_BASE_RC_TRY_AGAIN) {
251         LOG_DEBUG("A layer below returned TRY_AGAIN: %" PRIx32, r);
252         esysContext->state = _ESYS_STATE_SENT;
253         goto error_cleanup;
254     }
255     /* This block handle the resubmission of TPM commands given a certain set of
256      * TPM response codes. */
257     if (r == TPM2_RC_RETRY || r == TPM2_RC_TESTING || r == TPM2_RC_YIELDED) {
258         LOG_DEBUG("TPM returned RETRY, TESTING or YIELDED, which triggers a "
259             "resubmission: %" PRIx32, r);
260         if (esysContext->submissionCount++ >= _ESYS_MAX_SUBMISSIONS) {
261             LOG_WARNING("Maximum number of (re)submissions has been reached.");
262             esysContext->state = _ESYS_STATE_INIT;
263             goto error_cleanup;
264         }
265         esysContext->state = _ESYS_STATE_RESUBMISSION;
266         r = Tss2_Sys_ExecuteAsync(esysContext->sys);
267         if (r != TSS2_RC_SUCCESS) {
268             LOG_WARNING("Error attempting to resubmit");
269             /* We do not set esysContext->state here but inherit the most recent
270              * state of the _async function. */
271             goto error_cleanup;
272         }
273         r = TSS2_ESYS_RC_TRY_AGAIN;
274         LOG_DEBUG("Resubmission initiated and returning RC_TRY_AGAIN.");
275         goto error_cleanup;
276     }
277     /* The following is the "regular error" handling. */
278     if (iesys_tpm_error(r)) {
279         LOG_WARNING("Received TPM Error");
280         esysContext->state = _ESYS_STATE_INIT;
281         goto error_cleanup;
282     } else if (r != TSS2_RC_SUCCESS) {
283         LOG_ERROR("Received a non-TPM Error");
284         esysContext->state = _ESYS_STATE_INTERNALERROR;
285         goto error_cleanup;
286     }
287 
288     /*
289      * Now the verification of the response (hmac check) and if necessary the
290      * parameter decryption have to be done.
291      */
292     r = iesys_check_response(esysContext);
293     goto_state_if_error(r, _ESYS_STATE_INTERNALERROR, "Error: check response",
294                         error_cleanup);
295 
296     /*
297      * After the verification of the response we call the complete function
298      * to deliver the result.
299      */
300     r = Tss2_Sys_FirmwareRead_Complete(esysContext->sys,
301                                        (fuData != NULL) ? *fuData : NULL);
302     goto_state_if_error(r, _ESYS_STATE_INTERNALERROR,
303                         "Received error from SAPI unmarshaling" ,
304                         error_cleanup);
305 
306     esysContext->state = _ESYS_STATE_INIT;
307 
308     return TSS2_RC_SUCCESS;
309 
310 error_cleanup:
311     if (fuData != NULL)
312         SAFE_FREE(*fuData);
313 
314     return r;
315 }
316