1 /* SPDX-License-Identifier: BSD-2-Clause */
2 /*******************************************************************************
3  * Copyright 2018-2019, 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 <string.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <errno.h>
15 
16 #include "tss2_fapi.h"
17 #include "ifapi_json_serialize.h"
18 #include "fapi_crypto.h"
19 #include "fapi_int.h"
20 #include "fapi_util.h"
21 #include "tss2_esys.h"
22 #define LOGMODULE fapi
23 #include "util/log.h"
24 #include "util/aux_util.h"
25 
26 /** One-Call function for Fapi_NvExtend
27  *
28  * Performs an extend operation on an NV index with the type extend.
29  *
30  * @param[in,out] context the FAPI context
31  * @param[in] nvPath The path to the NV index that is extended
32  * @param[in] data The data to extend on the NV index
33  * @param[in] dataSize The size of the data to extend. Must be smaller than
34  *            1024
35  * @param[in] logData A JSON representation of the data that is written to the
36  *        event log. May be NULL
37  *
38  * @retval TSS2_RC_SUCCESS: if the function call was a success.
39  * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context, nvPath, or data is NULL.
40  * @retval TSS2_FAPI_RC_BAD_CONTEXT: if context corruption is detected.
41  * @retval TSS2_FAPI_RC_BAD_PATH: if nvPath is not found.
42  * @retval TSS2_FAPI_RC_NV_WRONG_TYPE: if the NV is not an extendable index.
43  * @retval TSS2_FAPI_RC_POLICY_UNKNOWN: if the policy is unknown.
44  * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous
45  *         operation already pending.
46  * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be saved.
47  * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for
48  *         internal operations or return parameters.
49  * @retval TSS2_FAPI_RC_NO_TPM if FAPI was initialized in no-TPM-mode via its
50  *         config file.
51  * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
52  *         the function.
53  * @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found
54  *         during authorization.
55  * @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
56  * @retval TSS2_FAPI_RC_TRY_AGAIN if an I/O operation is not finished yet and
57  *         this function needs to be called again.
58  * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
59  * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a required authorization callback
60  *         is not set.
61  * @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
62  * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
63  */
64 TSS2_RC
Fapi_NvExtend(FAPI_CONTEXT * context,char const * nvPath,uint8_t const * data,size_t dataSize,char const * logData)65 Fapi_NvExtend(
66     FAPI_CONTEXT  *context,
67     char    const *nvPath,
68     uint8_t const *data,
69     size_t         dataSize,
70     char    const *logData)
71 {
72     LOG_TRACE("called for context:%p", context);
73 
74     TSS2_RC r, r2;
75 
76     /* Check for NULL parameters */
77     check_not_null(context);
78     check_not_null(nvPath);
79     check_not_null(data);
80 
81     /* Check whether TCTI and ESYS are initialized */
82     return_if_null(context->esys, "Command can't be executed in none TPM mode.",
83                    TSS2_FAPI_RC_NO_TPM);
84 
85     /* If the async state automata of FAPI shall be tested, then we must not set
86        the timeouts of ESYS to blocking mode.
87        During testing, the mssim tcti will ensure multiple re-invocations.
88        Usually however the synchronous invocations of FAPI shall instruct ESYS
89        to block until a result is available. */
90 #ifndef TEST_FAPI_ASYNC
91     r = Esys_SetTimeout(context->esys, TSS2_TCTI_TIMEOUT_BLOCK);
92     return_if_error_reset_state(r, "Set Timeout to blocking");
93 #endif /* TEST_FAPI_ASYNC */
94 
95     r = Fapi_NvExtend_Async(context, nvPath, data, dataSize, logData);
96     return_if_error_reset_state(r, "NV_Extend");
97 
98     do {
99         /* We wait for file I/O to be ready if the FAPI state automata
100            are in a file I/O state. */
101         r = ifapi_io_poll(&context->io);
102         return_if_error(r, "Something went wrong with IO polling");
103 
104         /* Repeatedly call the finish function, until FAPI has transitioned
105            through all execution stages / states of this invocation. */
106         r = Fapi_NvExtend_Finish(context);
107     } while ((r & ~TSS2_RC_LAYER_MASK) == TSS2_BASE_RC_TRY_AGAIN);
108 
109     /* Reset the ESYS timeout to non-blocking, immediate response. */
110     r2 = Esys_SetTimeout(context->esys, 0);
111     return_if_error(r2, "Set Timeout to non-blocking");
112 
113     return_if_error_reset_state(r, "NV_Extend");
114 
115     LOG_TRACE("finished");
116     return TSS2_RC_SUCCESS;
117 }
118 
119 /** Asynchronous function for Fapi_NvExtend
120  *
121  * Performs an extend operation on an NV index with the type extend.
122  *
123  * Call Fapi_NvExtend_Finish to finish the execution of this command.
124  *
125  * @param[in,out] context the FAPI context
126  * @param[in] nvPath The path to the NV index that is extended
127  * @param[in] data The data to extend on the NV index
128  * @param[in] dataSize The size of the data to extend. Must be smaller than
129  *            1024
130  * @param[in] logData A JSON representation of the data that is written to the
131  *            event log. May be NULL
132  *
133  * @retval TSS2_RC_SUCCESS: if the function call was a success.
134  * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context, nvPath, or data is NULL.
135  * @retval TSS2_FAPI_RC_BAD_CONTEXT: if context corruption is detected.
136  * @retval TSS2_FAPI_RC_BAD_VALUE: if dataSize is larger than 1024
137  * @retval TSS2_FAPI_RC_BAD_PATH: if nvPath is not found.
138  * @retval TSS2_FAPI_RC_NV_WRONG_TYPE: if the NV is not an extendable index.
139  * @retval TSS2_FAPI_RC_POLICY_UNKNOWN: if the policy is unknown.
140  * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous
141  *         operation already pending.
142  * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be saved.
143  * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for
144  *         internal operations or return parameters.
145  * @retval TSS2_FAPI_RC_NO_TPM if FAPI was initialized in no-TPM-mode via its
146  *         config file.
147  * @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found
148  *         during authorization.
149  * @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
150  */
151 TSS2_RC
Fapi_NvExtend_Async(FAPI_CONTEXT * context,char const * nvPath,uint8_t const * data,size_t dataSize,char const * logData)152 Fapi_NvExtend_Async(
153     FAPI_CONTEXT  *context,
154     char    const *nvPath,
155     uint8_t const *data,
156     size_t         dataSize,
157     char    const *logData)
158 {
159     LOG_TRACE("called for context:%p", context);
160     LOG_TRACE("nvPath: %s", nvPath);
161     if (data) {
162         LOGBLOB_TRACE(data, dataSize, "data");
163     } else {
164         LOG_TRACE("data: (null) dataSize: %zi", dataSize);
165     }
166     LOG_TRACE("logData: %s", logData);
167 
168     TSS2_RC r;
169 
170     /* Check for NULL parameters */
171     check_not_null(context);
172     check_not_null(nvPath);
173     check_not_null(data);
174 
175     /* Check for maximum allowed dataSize. */
176     if (dataSize > 1024) {
177         LOG_ERROR("dataSize exceeds allowed maximum of 1024. dataSize = %zi", dataSize);
178         return TSS2_FAPI_RC_BAD_VALUE;
179     }
180 
181     /* Helpful alias pointers */
182     IFAPI_NV_Cmds * command = &context->nv_cmd;
183 
184     memset(command, 0 ,sizeof(IFAPI_NV_Cmds));
185 
186     /* Copy parameters to context for use during _Finish. */
187     uint8_t *in_data = malloc(dataSize);
188     goto_if_null2(in_data, "Out of memory", r, TSS2_FAPI_RC_MEMORY,
189             error_cleanup);
190     memcpy(in_data, data, dataSize);
191     command->data = in_data;
192     strdup_check(command->nvPath, nvPath, r, error_cleanup);
193     strdup_check(command->logData, logData, r, error_cleanup);
194     command->numBytes = dataSize;
195 
196     /* Reset all context-internal session state information. */
197     r = ifapi_session_init(context);
198     return_if_error(r, "Initialize NV_Extend");
199 
200     /* Load the nv index metadata from the keystore. */
201     r = ifapi_keystore_load_async(&context->keystore, &context->io, command->nvPath);
202     goto_if_error2(r, "Could not open: %s", error_cleanup, command->nvPath);
203 
204     /* Initialize the context state for this operation. */
205     context->state = NV_EXTEND_READ;
206     LOG_TRACE("finished");
207     return r;
208 
209 error_cleanup:
210     /* Cleanup duplicated input parameters that were copied before. */
211     SAFE_FREE(command->data);
212     SAFE_FREE(command->nvPath);
213     SAFE_FREE(command->logData);
214     return r;
215 }
216 
217 /** Asynchronous finish function for Fapi_NvExtend
218  *
219  * This function should be called after a previous Fapi_NvExtend.
220  *
221  * @param[in,out] context The FAPI_CONTEXT
222  *
223  * @retval TSS2_RC_SUCCESS: if the function call was a success.
224  * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context is NULL.
225  * @retval TSS2_FAPI_RC_BAD_CONTEXT: if context corruption is detected.
226  * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous
227  *         operation already pending.
228  * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be saved.
229  * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for
230  *         internal operations or return parameters.
231  * @retval TSS2_FAPI_RC_TRY_AGAIN: if the asynchronous operation is not yet
232  *         complete. Call this function again later.
233  * @retval TSS2_FAPI_RC_BAD_PATH if the used path in inappropriate-
234  * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
235  *         the function.
236  * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
237  * @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found
238  *         during authorization.
239  * @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
240  * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a required authorization callback
241  *         is not set.
242  * @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
243  * @retval TSS2_FAPI_RC_POLICY_UNKNOWN if policy search for a certain policy digest
244  *         was not successful.
245  * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
246  */
247 TSS2_RC
Fapi_NvExtend_Finish(FAPI_CONTEXT * context)248 Fapi_NvExtend_Finish(
249     FAPI_CONTEXT  *context)
250 {
251     LOG_TRACE("called for context:%p", context);
252 
253     TSS2_RC r;
254     ESYS_TR authIndex;
255     json_object *jso = NULL;
256     IFAPI_CRYPTO_CONTEXT_BLOB *cryptoContext = NULL;
257     TPMI_ALG_HASH hashAlg;
258     size_t hashSize;
259     ESYS_TR auth_session;
260 
261     /* Check for NULL parameters */
262     check_not_null(context);
263 
264     /* Helpful alias pointers */
265     IFAPI_NV_Cmds * command = &context->nv_cmd;
266     TPM2B_MAX_NV_BUFFER *auxData = (TPM2B_MAX_NV_BUFFER *)&context->aux_data;
267     ESYS_TR nvIndex = command->esys_handle;
268     const uint8_t *data = command->data;
269     IFAPI_OBJECT *object = &command->nv_object;
270     IFAPI_OBJECT *authObject = &command->auth_object;
271     IFAPI_EVENT *event = &command->pcr_event;
272 
273     switch (context->state) {
274     statecase(context->state, NV_EXTEND_READ)
275         /* First check whether the file in object store can be updated. */
276         r = ifapi_keystore_check_writeable(&context->keystore, &context->io, command->nvPath);
277         goto_if_error_reset_state(r, "Check whether update object store is possible.", error_cleanup);
278 
279         r = ifapi_keystore_load_finish(&context->keystore, &context->io, object);
280         return_try_again(r);
281         return_if_error_reset_state(r, "read_finish failed");
282 
283         if (object->objectType != IFAPI_NV_OBJ)
284             goto_error(r, TSS2_FAPI_RC_BAD_PATH, "%s is no NV object.", error_cleanup,
285                        command->nvPath);
286 
287         /* Initialize the NV index object for ESYS. */
288         r = ifapi_initialize_object(context->esys, object);
289         goto_if_error_reset_state(r, "Initialize NV object", error_cleanup);
290 
291         /* Store object info in context */
292         nvIndex = command->nv_object.handle;
293         command->esys_handle = context->nv_cmd.nv_object.handle;
294         command->nv_obj = object->misc.nv;
295 
296         /* Determine the kind of authorization to be used. */
297         if (object->misc.nv.public.nvPublic.attributes & TPMA_NV_PPWRITE) {
298             ifapi_init_hierarchy_object(authObject, ESYS_TR_RH_PLATFORM);
299             authIndex = ESYS_TR_RH_PLATFORM;
300         } else {
301             if (object->misc.nv.public.nvPublic.attributes & TPMA_NV_OWNERWRITE) {
302                 ifapi_init_hierarchy_object(authObject, ESYS_TR_RH_OWNER);
303                 authIndex = ESYS_TR_RH_OWNER;
304             } else {
305                 authIndex = nvIndex;
306             }
307             *authObject = *object;
308         }
309         command->auth_index = authIndex;
310         context->primary_state = PRIMARY_INIT;
311 
312         /* Start a session for authorization. */
313         r = ifapi_get_sessions_async(context,
314                                      IFAPI_SESSION_GENEK | IFAPI_SESSION1,
315                                      TPMA_SESSION_DECRYPT, 0);
316         goto_if_error_reset_state(r, "Create sessions", error_cleanup);
317 
318         fallthrough;
319 
320     statecase(context->state, NV_EXTEND_WAIT_FOR_SESSION)
321         r = ifapi_get_sessions_finish(context, &context->profiles.default_profile,
322                                       object->misc.nv.public.nvPublic.nameAlg);
323         return_try_again(r);
324         goto_if_error_reset_state(r, " FAPI create session", error_cleanup);
325 
326         if (command->numBytes > TPM2_MAX_NV_BUFFER_SIZE) {
327             goto_error_reset_state(r, TSS2_FAPI_RC_BAD_VALUE,
328                                    "Buffer for NvExtend is too large.",
329                                    error_cleanup);
330         }
331 
332         auxData->size = command->numBytes;
333         memcpy(&auxData->buffer[0], &data[0], auxData->size);
334         command->data_idx = auxData->size;
335         fallthrough;
336 
337     statecase(context->state, NV_EXTEND_AUTHORIZE)
338         /* Prepare the authorization data for the NV index. */
339         r = ifapi_authorize_object(context, authObject, &auth_session);
340         return_try_again(r);
341         goto_if_error(r, "Authorize NV object.", error_cleanup);
342 
343         /* Extend the data into the NV index. */
344         r = Esys_NV_Extend_Async(context->esys,
345                                  command->auth_index,
346                                  nvIndex,
347                                  auth_session,
348                                  ESYS_TR_NONE,
349                                  ESYS_TR_NONE,
350                                  auxData);
351         goto_if_error_reset_state(r, " Fapi_NvExtend_Async", error_cleanup);
352 
353         command->bytesRequested = auxData->size;
354         command->data = (uint8_t *)data;
355 
356         fallthrough;
357 
358     statecase(context->state, NV_EXTEND_AUTH_SENT)
359         r = Esys_NV_Extend_Finish(context->esys);
360         return_try_again(r);
361 
362         goto_if_error_reset_state(r, "FAPI NV_Extend_Finish", error_cleanup);
363 
364         command->numBytes -= context->nv_cmd.bytesRequested;
365 
366         /* Compute Digest of the current event */
367         hashAlg = object->misc.nv.public.nvPublic.nameAlg;
368         r = ifapi_crypto_hash_start(&cryptoContext, hashAlg);
369         return_if_error(r, "crypto hash start");
370 
371         HASH_UPDATE_BUFFER(cryptoContext,
372                            &auxData->buffer[0], auxData->size,
373                            r, error_cleanup);
374 
375         r = ifapi_crypto_hash_finish(&cryptoContext,
376                                      (uint8_t *)
377                                      &event->digests.digests[0].digest,
378                                      &hashSize);
379         return_if_error(r, "crypto hash finish");
380 
381         event->digests.digests[0].hashAlg = hashAlg;
382         event->digests.count = 1;
383         event->pcr = object->misc.nv.public.nvPublic.nvIndex;
384         event->type = IFAPI_TSS_EVENT_TAG;
385         memcpy(&event->sub_event.tss_event.data.buffer[0],
386                &auxData->buffer[0], auxData->size);
387         event->sub_event.tss_event.data.size = auxData->size;
388         if (command->logData) {
389             strdup_check(event->sub_event.tss_event.event, command->logData,
390                     r, error_cleanup);
391         } else {
392             event->sub_event.tss_event.event = NULL;
393         }
394 
395         /* Event log of the NV object has to be extended */
396         if (command->nv_object.misc.nv.event_log) {
397             command->jso_event_log
398                 = json_tokener_parse(command->nv_object.misc.nv.event_log);
399             goto_if_null2(command->jso_event_log, "Out of memory", r,
400                           TSS2_FAPI_RC_MEMORY,
401                           error_cleanup);
402             json_type jsoType = json_object_get_type(command->jso_event_log);
403             /* libjson-c does not deliver an array if array has only one element */
404             if (jsoType != json_type_array) {
405                 json_object *jsonArray = json_object_new_array();
406                 json_object_array_add(jsonArray, command->jso_event_log);
407                 command->jso_event_log = jsonArray;
408             }
409         } else {
410             /* First event */
411             command->jso_event_log = json_object_new_array();
412         }
413         command->pcr_event.recnum =
414             json_object_array_length(command->jso_event_log) + 1;
415 
416         r = ifapi_json_IFAPI_EVENT_serialize(&command->pcr_event, &jso);
417         goto_if_error(r, "Error serialize event", error_cleanup);
418 
419         json_object_array_add(command->jso_event_log, jso);
420         SAFE_FREE(object->misc.nv.event_log);
421         strdup_check(object->misc.nv.event_log,
422             json_object_to_json_string_ext(command->jso_event_log,
423                                                     JSON_C_TO_STRING_PRETTY),
424             r, error_cleanup);
425 
426         /* Perform esys serialization if necessary */
427         r = ifapi_esys_serialize_object(context->esys, &command->nv_object);
428         goto_if_error(r, "Prepare serialization", error_cleanup);
429 
430         /* Start writing the NV object to the key store */
431         r = ifapi_keystore_store_async(&context->keystore, &context->io,
432                                        command->nvPath,
433                                        &command->nv_object);
434 
435         goto_if_error_reset_state(r, "Could not open: %sh", error_cleanup,
436                                   command->nvPath);
437 
438         fallthrough;
439 
440     statecase(context->state, NV_EXTEND_WRITE)
441         /* Finish writing the NV object to the key store */
442         r = ifapi_keystore_store_finish(&context->keystore, &context->io);
443         return_try_again(r);
444         return_if_error_reset_state(r, "write_finish failed");
445         fallthrough;
446 
447     statecase(context->state, NV_EXTEND_CLEANUP)
448         /* Cleanup the session. */
449         r = ifapi_cleanup_session(context);
450         try_again_or_error_goto(r, "Cleanup", error_cleanup);
451 
452         context->state = _FAPI_STATE_INIT;
453         r = TSS2_RC_SUCCESS;
454 
455         break;
456 
457     statecasedefault(context->state);
458     }
459 
460 error_cleanup:
461     /* Cleanup any intermediate results and state stored in the context. */
462     if (command->jso_event_log)
463         json_object_put(command->jso_event_log);
464     ifapi_cleanup_ifapi_object(object);
465     ifapi_cleanup_ifapi_object(&context->loadKey.auth_object);
466     ifapi_cleanup_ifapi_object(context->loadKey.key_object);
467     ifapi_cleanup_ifapi_object(&context->createPrimary.pkey_object);
468     if (cryptoContext) {
469         ifapi_crypto_hash_abort(&cryptoContext);
470     }
471     if (event)
472         ifapi_cleanup_event(event);
473     SAFE_FREE(command->data);
474     SAFE_FREE(command->nvPath);
475     SAFE_FREE(command->logData);
476     SAFE_FREE(object->misc.nv.event_log);
477     ifapi_session_clean(context);
478     LOG_TRACE("finished");
479     return r;
480 }
481