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 <stdlib.h>
12 #include <errno.h>
13 #include <unistd.h>
14 #include <errno.h>
15 #include <string.h>
16
17 #include "tss2_fapi.h"
18 #include "fapi_int.h"
19 #include "fapi_util.h"
20 #include "tss2_esys.h"
21 #define LOGMODULE fapi
22 #include "util/log.h"
23 #include "util/aux_util.h"
24 #include "fapi_crypto.h"
25
26 /** One-Call function for Fapi_ChangeAuth
27 *
28 * Changes the Authorization data of an entity found at keyPath. The parameter
29 * authValue is a 0-terminated UTF-8 encoded password.
30 * If it is longer than the digest size of the entity's nameAlg, it will be
31 * hashed according the the TPM specification part 1, rev 138, section 19.6.4.3.
32 *
33 * @param[in,out] context The FAPI_CONTEXT
34 * @param[in] entityPath The path to the entity to modify
35 * @param[in] authValue The new 0-terminated password to set for the entity.
36 * 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 or entityPath is NULL.
40 * @retval TSS2_FAPI_RC_BAD_CONTEXT: if context corruption is detected.
41 * @retval TSS2_FAPI_RC_BAD_PATH: if entityPath does not map to a FAPI entity.
42 * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous
43 * operation already pending.
44 * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be saved.
45 * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for
46 * internal operations or return parameters.
47 * @retval TSS2_FAPI_RC_NO_TPM if FAPI was initialized in no-TPM-mode via its
48 * config file.
49 * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
50 * the function.
51 * @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found
52 * during authorization.
53 * @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
54 * @retval TSS2_FAPI_RC_TRY_AGAIN if an I/O operation is not finished yet and
55 * this function needs to be called again.
56 * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
57 * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a required authorization callback
58 * is not set.
59 * @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
60 * @retval TSS2_FAPI_RC_POLICY_UNKNOWN if policy search for a certain policy digest
61 * was not successful.
62 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
63 */
64 TSS2_RC
Fapi_ChangeAuth(FAPI_CONTEXT * context,char const * entityPath,char const * authValue)65 Fapi_ChangeAuth(
66 FAPI_CONTEXT *context,
67 char const *entityPath,
68 char const *authValue)
69 {
70 LOG_TRACE("called for context:%p", context);
71
72 TSS2_RC r, r2;
73
74 /* Check for NULL parameters */
75 check_not_null(context);
76 check_not_null(entityPath);
77
78 /* Check whether TCTI and ESYS are initialized */
79 return_if_null(context->esys, "Command can't be executed in none TPM mode.",
80 TSS2_FAPI_RC_NO_TPM);
81
82 /* If the async state automata of FAPI shall be tested, then we must not set
83 the timeouts of ESYS to blocking mode.
84 During testing, the mssim tcti will ensure multiple re-invocations.
85 Usually however the synchronous invocations of FAPI shall instruct ESYS
86 to block until a result is available. */
87 #ifndef TEST_FAPI_ASYNC
88 r = Esys_SetTimeout(context->esys, TSS2_TCTI_TIMEOUT_BLOCK);
89 return_if_error_reset_state(r, "Set Timeout to blocking");
90 #endif /* TEST_FAPI_ASYNC */
91
92 r = Fapi_ChangeAuth_Async(context, entityPath, authValue);
93 return_if_error_reset_state(r, "Entity_ChangeAuth");
94
95 do {
96 /* We wait for file I/O to be ready if the FAPI state automata
97 are in a file I/O state. */
98 r = ifapi_io_poll(&context->io);
99 return_if_error(r, "Something went wrong with IO polling");
100
101 /* Repeatedly call the finish function, until FAPI has transitioned
102 through all execution stages / states of this invocation. */
103 r = Fapi_ChangeAuth_Finish(context);
104 } while ((r & ~TSS2_RC_LAYER_MASK) == TSS2_BASE_RC_TRY_AGAIN);
105
106 /* Reset the ESYS timeout to non-blocking, immediate response. */
107 r2 = Esys_SetTimeout(context->esys, 0);
108 return_if_error(r2, "Set Timeout to non-blocking");
109
110 return_if_error_reset_state(r, "Entity_ChangeAuth");
111
112 LOG_TRACE("finished");
113 return TSS2_RC_SUCCESS;
114 }
115
116 /** Asynchronous function for Fapi_ChangeAuth
117 *
118 * Changes the Authorization data of an entity found at keyPath. The parameter
119 * authValue is a 0-terminated UTF-8 encoded password.
120 * If it is longer than the digest size of the entity's nameAlg, it will be
121 * hashed according the the TPM specification part 1, rev 138, section 19.6.4.3.
122 *
123 * Call Fapi_ChangeAuth_Finish to finish the execution of this command.
124
125 * @param[in,out] context The FAPI_CONTEXT
126 * @param[in] entityPath The path to the entity to modify
127 * @param[in] authValue The new 0-terminated password to set for the entity.
128 * May be NULL
129 *
130 * @retval TSS2_RC_SUCCESS: if the function call was a success.
131 * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context or entityPath is NULL.
132 * @retval TSS2_FAPI_RC_BAD_CONTEXT: if context corruption is detected.
133 * @retval TSS2_FAPI_RC_BAD_PATH: if entityPath does not map to a FAPI entity.
134 * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous
135 * operation already pending.
136 * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be saved.
137 * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for
138 * internal operations or return parameters.
139 * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
140 * the function.
141 * @retval TSS2_FAPI_RC_NO_TPM if FAPI was initialized in no-TPM-mode via its
142 * config file.
143 * @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found
144 * during authorization.
145 * @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
146 */
147 TSS2_RC
Fapi_ChangeAuth_Async(FAPI_CONTEXT * context,char const * entityPath,char const * authValue)148 Fapi_ChangeAuth_Async(
149 FAPI_CONTEXT *context,
150 char const *entityPath,
151 char const *authValue)
152 {
153 LOG_TRACE("called for context:%p", context);
154 LOG_TRACE("entityPath: %s", entityPath);
155 LOG_TRACE("authValue: %s", authValue);
156
157 TSS2_RC r;
158
159 /* Check for NULL parameters */
160 check_not_null(context);
161 check_not_null(entityPath);
162
163 /* Helpful pointer aliases */
164 IFAPI_Entity_ChangeAuth * command = &(context->cmd.Entity_ChangeAuth);
165
166 /* Reset all context-internal session state information. */
167 r = ifapi_session_init(context);
168 return_if_error(r, "Initialize Entity_ChangeAuth");
169
170 /* Copy parameters to context for use during _Finish. */
171 context->loadKey.parent_handle = ESYS_TR_NONE;
172 command->handle = ESYS_TR_NONE;
173 memset(&command->object, 0, sizeof(IFAPI_OBJECT));
174 strdup_check(command->entityPath, entityPath, r, error_cleanup);
175 if (authValue != NULL) {
176 strdup_check(command->authValue, authValue, r, error_cleanup);
177 } else {
178 strdup_check(command->authValue, "", r, error_cleanup);
179 }
180 command->handle = ESYS_TR_NONE;
181
182 /* Get a session for further authorizing and integrity checking the
183 subsequent ChangeAuth calls. */
184 r = ifapi_get_sessions_async(context,
185 IFAPI_SESSION_GENEK | IFAPI_SESSION1,
186 TPMA_SESSION_DECRYPT, 0);
187 goto_if_error_reset_state(r, "Create sessions", error_cleanup);
188
189 /* Copy new auth value to appropriate structure in context */
190 if (command->authValue) {
191 if (strlen(command->authValue) > sizeof(TPMU_HA)) {
192 LOG_ERROR("authValue to big. (Should be <= %zu", sizeof(TPMU_HA));
193 r = TSS2_FAPI_RC_BAD_VALUE;
194 goto error_cleanup;
195 }
196
197 command->newAuthValue.size =
198 strlen(command->authValue);
199 memcpy(&command->newAuthValue.buffer[0],
200 command->authValue,
201 command->newAuthValue.size);
202 } else {
203 command->newAuthValue.size = 0;
204 }
205
206 /* Initialize the context state for this operation. */
207 context->state = ENTITY_CHANGE_AUTH_WAIT_FOR_SESSION;
208
209 LOG_TRACE("finished");
210 return TSS2_RC_SUCCESS;
211
212 error_cleanup:
213 /* Cleanup duplicated input parameters that were copied before. */
214 SAFE_FREE(command->entityPath);
215 SAFE_FREE(command->authValue);
216 return r;
217 }
218
219 /** Asynchronous finish function for Fapi_ChangeAuth
220 *
221 * This function should be called after a previous Fapi_ChangeAuth_Async.
222 *
223 * @param[in,out] context The FAPI_CONTEXT
224 *
225 * @retval TSS2_RC_SUCCESS: if the function call was a success.
226 * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context is NULL.
227 * @retval TSS2_FAPI_RC_BAD_CONTEXT: if context corruption is detected.
228 * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous
229 * operation already pending.
230 * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be saved.
231 * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for
232 * internal operations or return parameters.
233 * @retval TSS2_FAPI_RC_TRY_AGAIN: if the asynchronous operation is not yet
234 * complete. Call this function again later.
235 * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
236 * the function.
237 * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
238 * @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found
239 * during authorization.
240 * @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
241 * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a required authorization callback
242 * is not set.
243 * @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
244 * @retval TSS2_FAPI_RC_POLICY_UNKNOWN if policy search for a certain policy digest
245 * was not successful.
246 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
247 */
248 TSS2_RC
Fapi_ChangeAuth_Finish(FAPI_CONTEXT * context)249 Fapi_ChangeAuth_Finish(
250 FAPI_CONTEXT *context)
251 {
252 LOG_TRACE("called for context:%p", context);
253
254 TSS2_RC r;
255 ESYS_TR auth_session;
256
257 /* Check for NULL parameters */
258 check_not_null(context);
259
260 /* Helpful pointers */
261 IFAPI_Entity_ChangeAuth * command = &(context->cmd.Entity_ChangeAuth);
262 IFAPI_OBJECT * object = &command->object;
263 const IFAPI_PROFILE *profile;
264
265 switch (context->state) {
266 statecase(context->state, ENTITY_CHANGE_AUTH_WAIT_FOR_SESSION)
267 /* Retrieve profile information for subsequent commands. */
268 r = ifapi_profiles_get(&context->profiles, command->entityPath,
269 &profile);
270 goto_if_error_reset_state(r, " FAPI create session", error_cleanup);
271
272 /* Finish starting the session establishment. */
273 r = ifapi_get_sessions_finish(context, profile, profile->nameAlg);
274 return_try_again(r);
275
276 goto_if_error_reset_state(r, " FAPI create session", error_cleanup);
277
278 /* If the referenced entity is an NV-Index, load its metadata from
279 the keystore. */
280 if (ifapi_path_type_p(command->entityPath,
281 IFAPI_NV_PATH)) {
282 r = ifapi_keystore_load_async(&context->keystore, &context->io,
283 command->entityPath);
284 return_if_error_reset_state(r, "Could not open: %s",
285 command->entityPath);
286
287 /* Set the correct re-entry state for handling NV-index entities. */
288 context->state = ENTITY_CHANGE_AUTH_WAIT_FOR_NV_READ;
289 return TSS2_FAPI_RC_TRY_AGAIN;
290 }
291
292 /* Check if the referenced entity is a hierarchy. */
293 command->hierarchy_handle =
294 ifapi_get_hierary_handle(command->entityPath);
295
296 if (command->hierarchy_handle) {
297 /* Set the correct re-entry state for handling hierarchies. */
298 context->state = ENTITY_CHANGE_AUTH_HIERARCHY_READ;
299 /* Load the hierarchy's metadata from the keystore. */
300 r = ifapi_keystore_load_async(&context->keystore, &context->io,
301 command->entityPath);
302 return_if_error_reset_state(r, "Could not open: %s",
303 command->entityPath);
304
305 return TSS2_FAPI_RC_TRY_AGAIN;
306 }
307
308 /* At this point, the referenced entity must be a key.
309 Load the key's metadata from the keystore. */
310 r = ifapi_load_keys_async(context, command->entityPath);
311 goto_if_error(r, "Load keys.", error_cleanup);
312
313 fallthrough;
314
315 statecase(context->state, ENTITY_CHANGE_AUTH_WAIT_FOR_KEY)
316 r = ifapi_load_keys_finish(context, IFAPI_NOT_FLUSH_PARENT,
317 &command->handle,
318 &command->key_object);
319 return_try_again(r);
320
321 fallthrough;
322
323 statecase(context->state, ENTITY_CHANGE_AUTH_WAIT_FOR_KEY_AUTH)
324 /* Authorize the object with the old authorization */
325 object = command->key_object;
326 r = ifapi_authorize_object(context, object, &auth_session);
327 return_try_again(r);
328 goto_if_error_reset_state(r, "Authorize key.", error_cleanup);
329
330 /* Call to change the Authorization of the key. */
331 r = Esys_ObjectChangeAuth_Async(context->esys,
332 command->handle,
333 context->loadKey.parent_handle,
334 auth_session,
335 ESYS_TR_NONE, ESYS_TR_NONE,
336 &command->newAuthValue);
337 goto_if_error(r, "Error: Sign", error_cleanup);
338
339 fallthrough;
340
341 statecase(context->state, ENTITY_CHANGE_AUTH_AUTH_SENT)
342 r = Esys_ObjectChangeAuth_Finish(context->esys,
343 &command->newPrivate);
344 return_try_again(r);
345
346 goto_if_error(r, "Error: Entity ChangeAuth", error_cleanup);
347
348 object = command->key_object;
349 object->misc.key.private.size = command->newPrivate->size;
350
351 /* Store the new private key blob to the context to be stored to
352 the keystore. */
353 free(object->misc.key.private.buffer);
354 object->misc.key.private.buffer = malloc(object->misc.key.private.size);
355 goto_if_null2(object->misc.key.private.buffer, "Out of memory.",
356 r, TSS2_FAPI_RC_MEMORY, error_cleanup);
357
358 memcpy(object->misc.key.private.buffer,
359 &command->newPrivate->buffer[0],
360 object->misc.key.private.size);
361 free(command->newPrivate);
362
363 /* Flush the key with the old authorization form the TPM. */
364 r = Esys_FlushContext_Async(context->esys,
365 command->handle);
366 goto_if_error(r, "Error: FlushContext", error_cleanup);
367
368 command->handle = ESYS_TR_NONE;
369
370 fallthrough;
371
372 statecase(context->state, ENTITY_CHANGE_AUTH_WAIT_FOR_FLUSH)
373 r = Esys_FlushContext_Finish(context->esys);
374 return_try_again(r);
375
376 goto_if_error(r, "Error: ObjectChangeAuth", error_cleanup);
377
378 /* Flush the parent key as well. */
379 if (!context->loadKey.parent_handle_persistent
380 && context->loadKey.parent_handle != ESYS_TR_NONE) {
381 r = Esys_FlushContext_Async(context->esys, context->loadKey.parent_handle);
382 goto_if_error(r, "Flush parent", error_cleanup);
383
384 context->loadKey.parent_handle = ESYS_TR_NONE;
385 return TSS2_FAPI_RC_TRY_AGAIN;
386 }
387
388 /* Store information about whether the new authorization is an
389 empty authorization or an actual password. */
390 object = command->key_object;
391
392 if (strlen(command->authValue) > 0)
393 object->misc.key.with_auth = TPM2_YES;
394 else
395 object->misc.key.with_auth = TPM2_NO;
396 fallthrough;
397
398 statecase(context->state, ENTITY_CHANGE_AUTH_WRITE_PREPARE)
399 /* Perform serialization of the esys object if necessary */
400 r = ifapi_esys_serialize_object(context->esys, object);
401 goto_if_error(r, "Prepare serialization", error_cleanup);
402
403 /* Start writing the NV object to the key store */
404 r = ifapi_keystore_store_async(&context->keystore, &context->io,
405 command->entityPath,
406 object);
407 goto_if_error_reset_state(r, "Could not open: %sh", error_cleanup,
408 command->entityPath);
409 fallthrough;
410
411 statecase(context->state, ENTITY_CHANGE_AUTH_WRITE)
412 /* Finish writing the object to the key store */
413 r = ifapi_keystore_store_finish(&context->keystore, &context->io);
414 return_try_again(r);
415 return_if_error_reset_state(r, "write_finish failed");
416
417 fallthrough;
418
419 statecase(context->state, ENTITY_CHANGE_AUTH_CLEANUP)
420 /* Clean up the session information and reset the state and be done. */
421 r = ifapi_cleanup_session(context);
422 try_again_or_error_goto(r, "Cleanup", error_cleanup);
423
424 context->state = _FAPI_STATE_INIT;
425 LOG_TRACE("success");
426 break;
427
428 statecase(context->state, ENTITY_CHANGE_AUTH_WAIT_FOR_NV_READ)
429 /* The is the re-entry in case of an NV-index as referenced object.
430 All code between the check for the entity type above and this
431 place was skipped in case of an NV-index. */
432 r = ifapi_keystore_load_finish(&context->keystore, &context->io,
433 &command->object);
434 return_try_again(r);
435 return_if_error_reset_state(r, "read_finish failed");
436
437 /* Initialize the esys-object for the NV-index. */
438 r = ifapi_initialize_object(context->esys, &command->object);
439 goto_if_error_reset_state(r, "Initialize NV object", error_cleanup);
440
441 fallthrough;
442
443 statecase(context->state, ENTITY_CHANGE_AUTH_WAIT_FOR_NV_AUTH)
444 /* Authorize the object with with the policies
445 auth value and command code */
446 r = ifapi_authorize_object(context, object, &auth_session);
447 return_try_again(r);
448 goto_if_error(r, "Authorize NV object.", error_cleanup);
449
450 /* Change the NV index's AuthValue. */
451 r = Esys_NV_ChangeAuth_Async(context->esys,
452 context->nv_cmd.nv_object.handle,
453 auth_session,
454 ESYS_TR_NONE,
455 ESYS_TR_NONE,
456 &command->newAuthValue);
457 goto_if_error(r, "Error: NV_ChangeAuth", error_cleanup);
458
459 fallthrough;
460
461 statecase(context->state, ENTITY_CHANGE_AUTH_WAIT_FOR_NV_CHANGE_AUTH)
462 r = Esys_NV_ChangeAuth_Finish(context->esys);
463 return_try_again(r);
464
465 goto_if_error(r, "Error: Entity ChangeAuth", error_cleanup);
466
467 /* Update the information about whether the new Auth is an empty
468 authorization or an actual password. */
469 if (strlen(command->authValue) > 0)
470 object->misc.nv.with_auth = TPM2_YES;
471 else
472 object->misc.nv.with_auth = TPM2_NO;
473
474 /* Jump over to the AUTH_WRITE_PREPARE state for storing the
475 new metadata to the keystore. */
476 context->state = ENTITY_CHANGE_AUTH_WRITE_PREPARE;
477 return TSS2_FAPI_RC_TRY_AGAIN;
478
479 statecase(context->state, ENTITY_CHANGE_AUTH_HIERARCHY_READ)
480 /* This is the re-entry point if the referenced entity is a
481 hierarchy. All code between the check for the entity type
482 and this place is skipped in case of a hierarchy. */
483 r = ifapi_keystore_load_finish(&context->keystore, &context->io, object);
484 return_try_again(r);
485 return_if_error_reset_state(r, "read_finish failed");
486
487 /* Initialize the esys object for the hierarhcy. */
488 r = ifapi_initialize_object(context->esys, &command->object);
489 goto_if_error_reset_state(r, "Initialize NV object", error_cleanup);
490
491 command->object.handle
492 = command->hierarchy_handle;
493
494 fallthrough;
495
496 statecase(context->state, ENTITY_CHANGE_AUTH_HIERARCHY_AUTHORIZE)
497 /* Authorize against the hierarhcy. */
498 r = ifapi_authorize_object(context, &command->object, &auth_session);
499 return_try_again(r);
500 goto_if_error(r, "Authorize hierarchy.", error_cleanup);
501
502 fallthrough;
503
504 statecase(context->state, ENTITY_CHANGE_AUTH_HIERARCHY_CHANGE_AUTH)
505 /* Change the hierarchy authorization. */
506 r = ifapi_change_auth_hierarchy(context,
507 command->hierarchy_handle,
508 &command->object,
509 &command->newAuthValue);
510 return_try_again(r);
511 goto_if_error(r, "Change auth hierarchy.", error_cleanup);
512
513 /* Jump over to the AUTH_WRITE_PREPARE state for storing the
514 new metadata to the keystore. */
515 context->state = ENTITY_CHANGE_AUTH_WRITE_PREPARE;
516 return TSS2_FAPI_RC_TRY_AGAIN;
517
518 statecasedefault(context->state);
519 }
520
521 error_cleanup:
522 /* In error cases object might not be flushed. */
523 if (context->loadKey.parent_handle != ESYS_TR_NONE)
524 Esys_FlushContext(context->esys, context->loadKey.parent_handle);
525 if (command->handle != ESYS_TR_NONE)
526 Esys_FlushContext(context->esys, command->handle);
527 ifapi_session_clean(context);
528 ifapi_cleanup_ifapi_object(object);
529 ifapi_cleanup_ifapi_object(context->loadKey.key_object);
530 ifapi_cleanup_ifapi_object(&context->loadKey.auth_object);
531 ifapi_cleanup_ifapi_object(&context->createPrimary.pkey_object);
532 ifapi_cleanup_ifapi_object(command->key_object);
533 SAFE_FREE(command->entityPath);
534 SAFE_FREE(command->authValue);
535 LOG_TRACE("finished");
536 return r;
537 }
538