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