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 <string.h>
13 #include <unistd.h>
14 #include <errno.h>
15 
16 #include "tss2_fapi.h"
17 #include "fapi_int.h"
18 #include "fapi_util.h"
19 #include "tss2_esys.h"
20 #include "ifapi_json_serialize.h"
21 #include "ifapi_json_deserialize.h"
22 #define LOGMODULE fapi
23 #include "util/log.h"
24 #include "util/aux_util.h"
25 
26 
27 /** One-Call function for Fapi_Delete
28  *
29  * Deletes a given key, policy or NV index from the system.
30  *
31  * @param[in,out] context The ESAPI_CONTEXT
32  * @param[in] path The path to the entity that is to be deleted
33  *
34  * @retval TSS2_RC_SUCCESS: if the function call was a success.
35  * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context or path is NULL.
36  * @retval TSS2_FAPI_RC_BAD_CONTEXT: if context corruption is detected.
37  * @retval TSS2_FAPI_RC_BAD_PATH: if path does not map to a FAPI entity.
38  * @retval TSS2_FAPI_RC_NOT_DELETABLE: if the entity is not deletable or the
39  *         path is read-only.
40  * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous
41  *         operation already pending.
42  * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be saved.
43  * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for
44  *         internal operations or return parameters.
45  * @retval TSS2_FAPI_RC_NO_TPM if FAPI was initialized in no-TPM-mode via its
46  *         config file.
47  * @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found
48  *         during authorization.
49  * @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
50  * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
51  *         the function.
52  * @retval TSS2_FAPI_RC_TRY_AGAIN if an I/O operation is not finished yet and
53  *         this function needs to be called again.
54  * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
55  * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a required authorization callback
56  *         is not set.
57  * @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
58  * @retval TSS2_FAPI_RC_POLICY_UNKNOWN if policy search for a certain policy digest
59  *         was not successful.
60  * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
61  */
62 TSS2_RC
Fapi_Delete(FAPI_CONTEXT * context,char const * path)63 Fapi_Delete(
64     FAPI_CONTEXT   *context,
65     char     const *path)
66 {
67     LOG_TRACE("called for context:%p", context);
68 
69     TSS2_RC r;
70 
71     /* Check for NULL parameters */
72     check_not_null(context);
73     check_not_null(path);
74 
75     r = Fapi_Delete_Async(context, path);
76     return_if_error_reset_state(r, "Entity_Delete");
77 
78     do {
79         /* We wait for file I/O to be ready if the FAPI state automata
80            are in a file I/O state. */
81         r = ifapi_io_poll(&context->io);
82         return_if_error(r, "Something went wrong with IO polling");
83 
84         /* Repeatedly call the finish function, until FAPI has transitioned
85            through all execution stages / states of this invocation. */
86         r = Fapi_Delete_Finish(context);
87     } while ((r & ~TSS2_RC_LAYER_MASK) == TSS2_BASE_RC_TRY_AGAIN);
88 
89     return_if_error_reset_state(r, "Entity_Delete");
90 
91     return TSS2_RC_SUCCESS;
92 }
93 
94 /** Asynchronous function for Fapi_Delete
95  *
96  * Deletes a given key, policy or NV index from the system.
97 
98  * Call Fapi_Delete_Finish to finish the execution of this command.
99  *
100  * @param[in,out] context The ESAPI_CONTEXT
101  * @param[in] path The path to the entity that is to be deleted
102  *
103  * @retval TSS2_RC_SUCCESS: if the function call was a success.
104  * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context or path is NULL.
105  * @retval TSS2_FAPI_RC_BAD_CONTEXT: if context corruption is detected.
106  * @retval TSS2_FAPI_RC_BAD_PATH: if path does not map to a FAPI entity.
107  * @retval TSS2_FAPI_RC_NOT_DELETABLE: if the entity is not deletable or the
108  *         path is read-only.
109  * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous
110  *         operation already pending.
111  * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be saved.
112  * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for
113  *         internal operations or return parameters.
114  * @retval TSS2_FAPI_RC_NO_TPM if FAPI was initialized in no-TPM-mode via its
115  *         config file.
116  * @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found
117  *         during authorization.
118  * @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
119  * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
120  *         the function.
121  * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
122  */
123 TSS2_RC
Fapi_Delete_Async(FAPI_CONTEXT * context,char const * path)124 Fapi_Delete_Async(
125     FAPI_CONTEXT   *context,
126     char     const *path)
127 {
128     LOG_TRACE("called for context:%p", context);
129     LOG_TRACE("path: %s", path);
130 
131     TSS2_RC r;
132 
133     /* Check for NULL parameters */
134     check_not_null(context);
135     check_not_null(path);
136 
137     /* Helpful alias pointers */
138     IFAPI_Entity_Delete * command = &(context->cmd.Entity_Delete);
139     IFAPI_OBJECT *object = &command->object;
140     IFAPI_OBJECT *authObject = &command->auth_object;
141 
142     /* Copy parameters to context for use during _Finish. */
143     strdup_check(command->path, path, r, error_cleanup);
144 
145     /* List all keystore elements in the path hierarchy of the provided
146        path. The last of these is the object to be deleted. */
147     r = ifapi_keystore_list_all(&context->keystore, path, &command->pathlist,
148                                &command->numPaths);
149     return_if_error(r, "get entities.");
150 
151     command->path_idx = command->numPaths;
152 
153     if (command->numPaths == 0) {
154         goto_error(r, TSS2_FAPI_RC_BAD_PATH, "No objects found.", error_cleanup);
155     }
156 
157     object->objectType = IFAPI_OBJ_NONE;
158     authObject->objectType = IFAPI_OBJ_NONE;
159 
160     if (ifapi_path_type_p(path, IFAPI_EXT_PATH) ||
161         (ifapi_path_type_p(path, IFAPI_POLICY_PATH))) {
162         /* No session will be needed these files can be deleted without
163            interaction with the TPM */
164         r = ifapi_non_tpm_mode_init(context);
165         return_if_error(r, "Initialize Entity_Delete");
166 
167         context->state = ENTITY_DELETE_GET_FILE;
168     } else {
169         /* Check whether TCTI and ESYS are initialized */
170         return_if_null(context->esys, "Command can't be executed in none TPM mode.",
171                        TSS2_FAPI_RC_NO_TPM);
172 
173         /* If the async state automata of FAPI shall be tested, then we must not set
174            the timeouts of ESYS to blocking mode.
175            During testing, the mssim tcti will ensure multiple re-invocations.
176            Usually however the synchronous invocations of FAPI shall instruct ESYS
177            to block until a result is available. */
178 #ifndef TEST_FAPI_ASYNC
179         r = Esys_SetTimeout(context->esys, TSS2_TCTI_TIMEOUT_BLOCK);
180         return_if_error_reset_state(r, "Set Timeout to blocking");
181 #endif /* TEST_FAPI_ASYNC */
182 
183         /* A TPM session will be created to enable object authorization */
184         r = ifapi_session_init(context);
185         return_if_error(r, "Initialize Entity_Delete");
186 
187         r = ifapi_get_sessions_async(context,
188                                  IFAPI_SESSION_GENEK | IFAPI_SESSION1,
189                                  0, 0);
190         goto_if_error_reset_state(r, "Create sessions", error_cleanup);
191 
192         context->state = ENTITY_DELETE_WAIT_FOR_SESSION;
193     }
194 
195     LOG_TRACE("finished");
196     return TSS2_RC_SUCCESS;
197 
198 error_cleanup:
199     /* Cleanup any intermediate results and state stored in the context. */
200     SAFE_FREE(command->path);
201     if (Esys_FlushContext(context->esys, context->session1) != TSS2_RC_SUCCESS) {
202         LOG_ERROR("Cleanup session failed.");
203     }
204     return r;
205 }
206 
207 
208 /** Asynchronous finish function for Fapi_Delete
209  *
210  * This function should be called after a previous Fapi_Delete_Async.
211  *
212  * @param[in,out] context The FAPI_CONTEXT
213  *
214  * @retval TSS2_RC_SUCCESS: if the function call was a success.
215  * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context is NULL.
216  * @retval TSS2_FAPI_RC_BAD_CONTEXT: if context corruption is detected.
217  * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous
218  *         operation already pending.
219  * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be saved.
220  * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for
221  *         internal operations or return parameters.
222  * @retval TSS2_FAPI_RC_TRY_AGAIN: if the asynchronous operation is not yet
223  *         complete. Call this function again later.
224  * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
225  * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
226  *         the function.
227  * @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found
228  *         during authorization.
229  * @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
230  * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a required authorization callback
231  *         is not set.
232  * @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
233  * @retval TSS2_FAPI_RC_POLICY_UNKNOWN if policy search for a certain policy digest
234  *         was not successful.
235  * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
236  */
237 TSS2_RC
Fapi_Delete_Finish(FAPI_CONTEXT * context)238 Fapi_Delete_Finish(
239     FAPI_CONTEXT   *context)
240 {
241     LOG_TRACE("called for context:%p", context);
242 
243     TSS2_RC r;
244     ESYS_TR authIndex;
245     ESYS_TR auth_session;
246     char *path;
247 
248     /* Check for NULL parameters */
249     check_not_null(context);
250 
251     /* Helpful alias pointers */
252     IFAPI_Entity_Delete * command = &(context->cmd.Entity_Delete);
253     IFAPI_OBJECT *object = &command->object;
254     IFAPI_OBJECT *authObject = &command->auth_object;
255 
256     switch (context->state) {
257         statecase(context->state, ENTITY_DELETE_WAIT_FOR_SESSION);
258             /* If a TPM object (e.g. a persistent key) was referenced, then this
259                is the entry point. */
260         r = ifapi_get_sessions_finish(context, &context->profiles.default_profile,
261                                       context->profiles.default_profile.nameAlg);
262             return_try_again(r);
263             goto_if_error(r, "Create FAPI session.", error_cleanup);
264 
265             fallthrough;
266 
267         statecase(context->state, ENTITY_DELETE_GET_FILE);
268             /* If a non-TPM object (e.g. a policy) was referenced, then this is the
269                entry point. */
270             /* Use last path in the path list */
271             command->path_idx -= 1;
272             path = command->pathlist[command->path_idx];
273             LOG_TRACE("Delete object: %s %zu", path, command->path_idx);
274 
275             if (ifapi_path_type_p(path, IFAPI_EXT_PATH)) {
276                 /* External keyfile can be deleted directly without TPM operations. */
277                 context->state = ENTITY_DELETE_FILE;
278                 return TSS2_FAPI_RC_TRY_AGAIN;
279             }
280 
281             if (ifapi_path_type_p(path, IFAPI_POLICY_PATH)) {
282                 /* Policy file can be deleted directly without TPM operations. */
283                 context->state = ENTITY_DELETE_POLICY;
284                 return TSS2_FAPI_RC_TRY_AGAIN;
285             }
286 
287             /* Load the object metadata from the keystore. */
288             r = ifapi_keystore_load_async(&context->keystore, &context->io, path);
289             return_if_error2(r, "Could not open: %s", path);
290 
291             fallthrough;
292 
293         statecase(context->state, ENTITY_DELETE_READ);
294             /* We only end up in this path, if the referenced object requires
295                TPM operations; e.g. persistent key or NV index. */
296             r = ifapi_keystore_load_finish(&context->keystore, &context->io, object);
297             return_try_again(r);
298             return_if_error_reset_state(r, "read_finish failed");
299 
300             /* Initialize the ESYS object for the persistent key or NV Index. */
301             r = ifapi_initialize_object(context->esys, object);
302             goto_if_error_reset_state(r, "Initialize NV object", error_cleanup);
303 
304             if (object->objectType == IFAPI_KEY_OBJ) {
305                 /* If the object is a key, we jump over to ENTITY_DELETE_KEY. */
306                 command->is_key = true;
307                 context->state = ENTITY_DELETE_KEY;
308                 return TSS2_FAPI_RC_TRY_AGAIN;
309 
310             } else  if (object->objectType == IFAPI_NV_OBJ) {
311                 /* Prepare for the deletion of an NV index. */
312                 command->is_key = false;
313 
314                 if (object->misc.nv.hierarchy == ESYS_TR_RH_OWNER) {
315                     authIndex = ESYS_TR_RH_OWNER;
316                     ifapi_init_hierarchy_object(authObject, authIndex);
317                 } else {
318                     *authObject = *object;
319                     authIndex = object->handle;
320                 }
321                 command->auth_index = authIndex;
322                 context->state = ENTITY_DELETE_AUTHORIZE_NV;
323             } else {
324                 context->state = ENTITY_DELETE_FILE;
325                 return TSS2_FAPI_RC_TRY_AGAIN;
326             }
327             fallthrough;
328 
329         statecase(context->state, ENTITY_DELETE_AUTHORIZE_NV);
330             /* Authorize with the storage hierarhcy / "owner" to delete the NV index. */
331             r = ifapi_authorize_object(context, authObject, &auth_session);
332             return_try_again(r);
333             goto_if_error(r, "Authorize NV object.", error_cleanup);
334 
335             /* Delete the NV index. */
336             r = Esys_NV_UndefineSpace_Async(context->esys,
337                                             command->auth_index,
338                                             object->handle,
339                                             auth_session,
340                                             ESYS_TR_NONE,
341                                             ESYS_TR_NONE);
342             goto_if_error_reset_state(r, " Fapi_NV_UndefineSpace_Async", error_cleanup);
343 
344             context->state = ENTITY_DELETE_NULL_AUTH_SENT_FOR_NV;
345             return TSS2_FAPI_RC_TRY_AGAIN;
346 
347         statecase(context->state, ENTITY_DELETE_KEY);
348             if (object->misc.key.persistent_handle) {
349                 /* Delete the persistent handle from the TPM. */
350                 r = Esys_EvictControl_Async(context->esys, ESYS_TR_RH_OWNER,
351                                             object->handle,
352                                             context->session1,
353                                             ESYS_TR_NONE, ESYS_TR_NONE,
354                                             object->misc.key.persistent_handle);
355                 goto_if_error(r, "Evict Control", error_cleanup);
356                 context->state = ENTITY_DELETE_NULL_AUTH_SENT_FOR_KEY;
357             } else {
358                 context->state = ENTITY_DELETE_FILE;
359                 return TSS2_FAPI_RC_TRY_AGAIN;
360             }
361             fallthrough;
362 
363         statecase(context->state, ENTITY_DELETE_AUTH_SENT_FOR_KEY);
364             fallthrough;
365         statecase(context->state, ENTITY_DELETE_NULL_AUTH_SENT_FOR_KEY);
366             r = Esys_EvictControl_Finish(context->esys,
367                                          &command->new_object_handle);
368             return_try_again(r);
369             if ((r & ~TPM2_RC_N_MASK) == TPM2_RC_BAD_AUTH) {
370                 /* If evict control failed, we know that an owner password was set
371                    and we need to re-issue the command with a password being set. */
372                 if (context->state == ENTITY_DELETE_NULL_AUTH_SENT_FOR_KEY) {
373                     ifapi_init_hierarchy_object(authObject,
374                                                 TPM2_RH_OWNER);
375                     r = ifapi_set_auth(context, authObject,
376                                        "Owner Authorization");
377                     goto_if_error_reset_state(r, "Set owner authorization", error_cleanup);
378 
379                     context->state = ENTITY_DELETE_AUTH_SENT_FOR_KEY;
380                     return TSS2_FAPI_RC_TRY_AGAIN;
381                 }
382             }
383             goto_if_error_reset_state(r, "FAPI Entity_Delete", error_cleanup);
384 
385             context->state = ENTITY_DELETE_FILE;
386             return TSS2_FAPI_RC_TRY_AGAIN;
387             break;
388 
389         statecase(context->state, ENTITY_DELETE_AUTH_SENT_FOR_NV);
390             fallthrough;
391         statecase(context->state, ENTITY_DELETE_NULL_AUTH_SENT_FOR_NV);
392             r = Esys_NV_UndefineSpace_Finish(context->esys);
393             return_try_again(r);
394 
395             if ((r & ~TPM2_RC_N_MASK) == TPM2_RC_BAD_AUTH) {
396                 /* If undefine space failed, we know that an owner password was set
397                    and we need to re-issue the command with a password being set. */
398                 if (context->state == ENTITY_DELETE_NULL_AUTH_SENT_FOR_NV) {
399                     r = ifapi_set_auth(context, authObject, "Entity Delete object");
400                     goto_if_error_reset_state(r, " Fapi_NV_UndefineSpace", error_cleanup);
401 
402                     r = Esys_NV_UndefineSpace_Async(context->esys,
403                                                     command->auth_index,
404                                                     object->handle,
405                                                     context->session1,
406                                                     context->session2,
407                                                     ESYS_TR_NONE);
408                     goto_if_error_reset_state(r, "FAPI Entity_Delete", error_cleanup);
409 
410                     context->state = ENTITY_DELETE_AUTH_SENT_FOR_NV;
411                     return TSS2_FAPI_RC_TRY_AGAIN;
412                 }
413             }
414             goto_if_error_reset_state(r, "FAPI NV_UndefineSpace", error_cleanup);
415 
416             LOG_TRACE("NV Object undefined.");
417             context->state = ENTITY_DELETE_FILE;
418             return TSS2_FAPI_RC_TRY_AGAIN;
419             break;
420 
421         statecase(context->state, ENTITY_DELETE_POLICY);
422             /* This is the simple case of deleting a policy from the keystore. */
423             path = command->pathlist[command->path_idx];
424             LOG_TRACE("Delete: %s", path);
425 
426             r = ifapi_policy_delete(&context->pstore, path);
427             goto_if_error_reset_state(r, "Could not delete: %s", error_cleanup, path);
428 
429             if (command->path_idx > 0)
430                 context->state = ENTITY_DELETE_GET_FILE;
431             else
432                 context->state = ENTITY_DELETE_REMOVE_DIRS;
433             return TSS2_FAPI_RC_TRY_AGAIN;
434 
435         statecase(context->state, ENTITY_DELETE_FILE);
436             /* This is the simple case of deleting an external (pub)key from the keystore
437                or we enter here after the TPM operation for the persistent key or NV index
438                deletion have been performed. */
439             path = command->pathlist[command->path_idx];
440             LOG_TRACE("Delete: %s", path);
441             ifapi_cleanup_ifapi_object(object);
442             ifapi_cleanup_ifapi_object(authObject);
443 
444             /* Delete all the object's data from the keystore. */
445             r = ifapi_keystore_delete(&context->keystore, path);
446             goto_if_error_reset_state(r, "Could not delete: %s", error_cleanup, path);
447 
448             if (command->path_idx > 0) {
449                 context->state = ENTITY_DELETE_GET_FILE;
450                 return TSS2_FAPI_RC_TRY_AGAIN;
451             }
452 
453             fallthrough;
454 
455         statecase(context->state, ENTITY_DELETE_REMOVE_DIRS);
456             /* For some cases, we need to remove the directory that contained the
457                meta data as well. */
458             r = ifapi_keystore_remove_directories(&context->keystore, command->path);
459             goto_if_error(r, "Error while removing directories", error_cleanup);
460 
461             context->state = _FAPI_STATE_INIT;
462 
463             LOG_DEBUG("success");
464             r = TSS2_RC_SUCCESS;
465             break;
466 
467         statecasedefault(context->state);
468     }
469 
470     /* Reset the ESYS timeout to non-blocking, immediate response. */
471     if (context->esys) {
472         r = Esys_SetTimeout(context->esys, 0);
473         goto_if_error(r, "Set Timeout to non-blocking", error_cleanup);
474     }
475 
476     /* Cleanup intermediate state stored in the context. */
477     SAFE_FREE(command->path);
478     ifapi_cleanup_ifapi_object(authObject);
479     ifapi_cleanup_ifapi_object(object);
480     for (size_t i = 0; i < command->numPaths; i++) {
481         SAFE_FREE(command->pathlist[i]);
482     }
483     SAFE_FREE(command->pathlist);
484     ifapi_session_clean(context);
485     ifapi_cleanup_ifapi_object(&context->loadKey.auth_object);
486     ifapi_cleanup_ifapi_object(context->loadKey.key_object);
487     ifapi_cleanup_ifapi_object(&context->createPrimary.pkey_object);
488 
489     LOG_TRACE("finished");
490     return r;
491 
492 error_cleanup:
493     /* Cleanup any intermediate results and state stored in the context. */
494     Esys_SetTimeout(context->esys, 0);
495     ifapi_cleanup_ifapi_object(object);
496     SAFE_FREE(command->path);
497     for (size_t i = 0; i < command->numPaths; i++) {
498         SAFE_FREE(command->pathlist[i]);
499     }
500     SAFE_FREE(command->pathlist);
501     ifapi_session_clean(context);
502     ifapi_cleanup_ifapi_object(&context->createPrimary.pkey_object);
503     return r;
504 }
505