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 <stdio.h>
13 #include <unistd.h>
14 #include <stdlib.h>
15 #include <errno.h>
16
17 #include "tss2_fapi.h"
18 #include "fapi_int.h"
19 #include "fapi_util.h"
20 #include "fapi_policy.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_CreateNv
27 *
28 * This command creates an NV index in the TPM using a given path and type.
29 *
30 * @param[in,out] context The FAPI_CONTEXT
31 * @param[in] path The path to the new NV index
32 * @param[in] type The intended type of the new NV index. May be NULL
33 * @param[in] size The size of the new NV index in bytes. May be 0 if the size
34 * is inferred from the type
35 * @param[in] policyPath The path to the policy that is associated with the new
36 * NV index. May be NULL
37 * @param[in] authValue The authorization value that is associated with the new
38 * NV index. May be NULL
39 *
40 * @retval TSS2_RC_SUCCESS: if the function call was a success.
41 * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context or path is NULL.
42 * @retval TSS2_FAPI_RC_BAD_CONTEXT: if context corruption is detected.
43 * @retval TSS2_FAPI_RC_PATH_ALREADY_EXISTS: if an NV index already exists at
44 * path.
45 * @retval TSS2_FAPI_RC_BAD_VALUE: if type is non-NULL but invalid or does not
46 * match the size.
47 * @retval TSS2_FAPI_RC_BAD_PATH: if policyPath is non-NULL and does not map to
48 * a FAPI policy or if path dos not refer to a valid NV index path.
49 * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous
50 * operation already pending.
51 * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be saved.
52 * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for
53 * internal operations or return parameters.
54 * @retval TSS2_FAPI_RC_NO_TPM if FAPI was initialized in no-TPM-mode via its
55 * config file.
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_PATH_NOT_FOUND if a FAPI object path was not found
59 * during authorization.
60 * @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
61 * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
62 * @retval TSS2_FAPI_RC_NV_TOO_SMALL if too many NV handles are defined.
63 * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a required authorization callback
64 * is not set.
65 * @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
66 * @retval TSS2_FAPI_RC_POLICY_UNKNOWN if policy search for a certain policy digest
67 * was not successful.
68 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
69 */
70 TSS2_RC
Fapi_CreateNv(FAPI_CONTEXT * context,char const * path,char const * type,size_t size,char const * policyPath,char const * authValue)71 Fapi_CreateNv(
72 FAPI_CONTEXT *context,
73 char const *path,
74 char const *type,
75 size_t size,
76 char const *policyPath,
77 char const *authValue)
78 {
79 LOG_TRACE("called for context:%p", context);
80
81 TSS2_RC r, r2;
82
83 /* Check for NULL parameters */
84 check_not_null(context);
85 check_not_null(path);
86
87 /* Check whether TCTI and ESYS are initialized */
88 return_if_null(context->esys, "Command can't be executed in none TPM mode.",
89 TSS2_FAPI_RC_NO_TPM);
90
91 /* If the async state automata of FAPI shall be tested, then we must not set
92 the timeouts of ESYS to blocking mode.
93 During testing, the mssim tcti will ensure multiple re-invocations.
94 Usually however the synchronous invocations of FAPI shall instruct ESYS
95 to block until a result is available. */
96 #ifndef TEST_FAPI_ASYNC
97 r = Esys_SetTimeout(context->esys, TSS2_TCTI_TIMEOUT_BLOCK);
98 return_if_error_reset_state(r, "Set Timeout to blocking");
99 #endif /* TEST_FAPI_ASYNC */
100
101 r = Fapi_CreateNv_Async(context, path, type, size,
102 policyPath, authValue);
103 return_if_error_reset_state(r, "NV_CreateWithTemplate");
104
105 do {
106 /* We wait for file I/O to be ready if the FAPI state automata
107 are in a file I/O state. */
108 r = ifapi_io_poll(&context->io);
109 return_if_error(r, "Something went wrong with IO polling");
110
111 /* Repeatedly call the finish function, until FAPI has transitioned
112 through all execution stages / states of this invocation. */
113 r = Fapi_CreateNv_Finish(context);
114 } while ((r & ~TSS2_RC_LAYER_MASK) == TSS2_BASE_RC_TRY_AGAIN);
115
116 /* Reset the ESYS timeout to non-blocking, immediate response. */
117 r2 = Esys_SetTimeout(context->esys, 0);
118 return_if_error(r2, "Set Timeout to non-blocking");
119
120 return_if_error_reset_state(r, "NV_CreateWithTemplate");
121
122 return TSS2_RC_SUCCESS;
123 }
124
125 /** Asynchronous function for Fapi_CreateNv
126 *
127 * This command creates an NV index in the TPM using a given path and type.
128 *
129 * Call Fapi_CreateNv_Finish to finish the execution of this command.
130 *
131 * @param[in,out] context The FAPI_CONTEXT
132 * @param[in] path The path to the new NV index
133 * @param[in] type The intended type of the new NV index. May be NULL
134 * @param[in] size The size of the new NV index in bytes. May be 0 if the size
135 * is inferred from the type
136 * @param[in] policyPath The path to the policy that is associated with the new
137 * NV index. May be NULL
138 * @param[in] authValue The authorization value that is associated with the new
139 * NV index. May be NULL
140 *
141 * @retval TSS2_RC_SUCCESS: if the function call was a success.
142 * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context or path is NULL.
143 * @retval TSS2_FAPI_RC_BAD_CONTEXT: if context corruption is detected.
144 * @retval TSS2_FAPI_RC_PATH_ALREADY_EXISTS: if an NV index already exists at
145 * path.
146 * @retval TSS2_FAPI_RC_BAD_VALUE: if type is non-NULL but invalid or does not
147 * match the size.
148 * @retval TSS2_FAPI_RC_BAD_PATH: if policyPath is non-NULL and does not map to
149 * a FAPI policy or if path dos not refer to a valid NV index path.
150 * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous
151 * operation already pending.
152 * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be saved.
153 * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for
154 * internal operations or return parameters.
155 * @retval TSS2_FAPI_RC_NO_TPM if FAPI was initialized in no-TPM-mode via its
156 * config file.
157 */
158 TSS2_RC
Fapi_CreateNv_Async(FAPI_CONTEXT * context,char const * path,char const * type,size_t size,char const * policyPath,char const * authValue)159 Fapi_CreateNv_Async(
160 FAPI_CONTEXT *context,
161 char const *path,
162 char const *type,
163 size_t size,
164 char const *policyPath,
165 char const *authValue)
166 {
167 LOG_TRACE("called for context:%p", context);
168 LOG_TRACE("path: %s", path);
169 LOG_TRACE("type: %s", type);
170 LOG_TRACE("size: %zi", size);
171 LOG_TRACE("policyPath: %s", policyPath);
172 LOG_TRACE("authValue: %s", authValue);
173
174 TSS2_RC r;
175
176 /* Check for NULL parameters */
177 check_not_null(context);
178 check_not_null(path);
179
180 /* Helpful alias pointers */
181 IFAPI_NV_Cmds * nvCmd = &(context->nv_cmd);
182 TPM2B_AUTH *auth = &nvCmd->auth;
183 IFAPI_NV * miscNv = &(nvCmd->nv_object.misc.nv);
184
185 /* Reset all context-internal session state information. */
186 r = ifapi_session_init(context);
187 return_if_error(r, "Initialize NV_CreateNv");
188
189 /* First check whether an existing object would be overwritten */
190 r = ifapi_keystore_check_overwrite(&context->keystore, &context->io,
191 path);
192 return_if_error2(r, "Check overwrite %s", path);
193
194 /* Copy parameters to context for use during _Finish. */
195 memset(&context->nv_cmd, 0, sizeof(IFAPI_NV_Cmds));
196 if (authValue) {
197 if (strlen(authValue) > sizeof(TPMU_HA)) {
198 return_error(TSS2_FAPI_RC_BAD_VALUE, "AuthValue too long");
199 }
200
201 auth->size = strlen(authValue);
202 memcpy(&auth->buffer[0], authValue, auth->size);
203 } else {
204 auth->size = 0;
205 }
206 strdup_check(nvCmd->nvPath, path, r, error_cleanup);
207 nvCmd->numBytes = size;
208 nvCmd->nv_object.objectType = IFAPI_NV_OBJ;
209 strdup_check(miscNv->policyInstance, policyPath, r, error_cleanup);
210
211 /* Set the flags of the NV index to be created. If no type is given the empty-string
212 default type flags are set. */
213 r = ifapi_set_nv_flags(type ? type : "", &nvCmd->public_templ,
214 policyPath);
215 goto_if_error(r, "Set key flags for NV object", error_cleanup);
216
217 /* Initialize the context state for this operation. */
218 context->state = NV_CREATE_READ_PROFILE;
219 LOG_TRACE("finished");
220 return TSS2_RC_SUCCESS;
221
222 error_cleanup:
223 /* Cleanup duplicated input parameters that were copied before. */
224 SAFE_FREE(nvCmd->nvPath);
225 SAFE_FREE(miscNv->policyInstance);
226 return r;
227 }
228
229 /** Asynchronous finish function for Fapi_CreateNv
230 *
231 * This function should be called after a previous Fapi_CreateNv_Async.
232 *
233 * @param[in,out] context The FAPI_CONTEXT
234 *
235 * @retval TSS2_RC_SUCCESS: if the function call was a success.
236 * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context is NULL.
237 * @retval TSS2_FAPI_RC_BAD_CONTEXT: if context corruption is detected.
238 * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous
239 * operation already pending.
240 * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be saved.
241 * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for
242 * internal operations or return parameters.
243 * @retval TSS2_FAPI_RC_TRY_AGAIN: if the asynchronous operation is not yet
244 * complete. Call this function again later.
245 * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
246 * the function.
247 * @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found
248 * during authorization.
249 * @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
250 * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
251 * @retval TSS2_FAPI_RC_BAD_PATH if the used path in inappropriate-
252 * @retval TSS2_FAPI_RC_NV_TOO_SMALL if too many NV handles are defined.
253 * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a required authorization callback
254 * is not set.
255 * @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
256 * @retval TSS2_FAPI_RC_POLICY_UNKNOWN if policy search for a certain policy digest
257 * was not successful.
258 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
259 */
260 TSS2_RC
Fapi_CreateNv_Finish(FAPI_CONTEXT * context)261 Fapi_CreateNv_Finish(
262 FAPI_CONTEXT *context)
263 {
264 LOG_TRACE("called for context:%p", context);
265
266 TSS2_RC r;
267 ESYS_TR nvHandle;
268
269 /* Check for NULL parameters */
270 check_not_null(context);
271
272 /* Helpful alias pointers */
273 IFAPI_NV_Cmds * nvCmd = &(context->nv_cmd);
274 TPM2B_AUTH *auth = &nvCmd->auth;
275 IFAPI_OBJECT *hierarchy = &nvCmd->auth_object;
276 IFAPI_NV * miscNv = &(nvCmd->nv_object.misc.nv);
277 TPM2B_NV_PUBLIC *publicInfo = &miscNv->public;
278 TPM2B_DIGEST * authPolicy = &(miscNv->public.nvPublic.authPolicy);
279 TPMS_POLICY * policy = &(context->policy.policy);
280 TPMS_POLICY ** nvCmdPolicy = &nvCmd->nv_object.policy;
281 ESYS_TR auth_session;
282
283 switch (context->state) {
284 statecase(context->state, NV_CREATE_READ_PROFILE)
285 /* Mix the provided flags provided via the type with with template
286 of the current active crypto profile. */
287 r = ifapi_merge_profile_into_nv_template(context,
288 &nvCmd->public_templ);
289 goto_if_error_reset_state(r, "Merge profile", error_cleanup);
290
291 /* Store information from template in context */
292 miscNv->description = NULL;
293 publicInfo->nvPublic = nvCmd->public_templ.public;
294
295 /* Check that the hierarchy for the NV index to be created is "Owner".
296 FAPI does not allow the creation of "Platform" NV indexes. */
297 if (nvCmd->public_templ.hierarchy == TPM2_RH_OWNER) {
298 miscNv->hierarchy = ESYS_TR_RH_OWNER;
299 } else {
300 goto_error(r, TSS2_FAPI_RC_BAD_VALUE, "Wrong hierarchy", error_cleanup);
301 }
302
303 /* Load the Storage Hierarchy "Owner" meta data for used during
304 NV creation authorization. */
305 r = ifapi_keystore_load_async(&context->keystore, &context->io, "HS");
306 return_if_error_reset_state(r, "Could not open storage hierarchy HS");
307 fallthrough;
308
309 statecase(context->state, NV_CREATE_READ_HIERARCHY)
310 r = ifapi_keystore_load_finish(&context->keystore, &context->io,
311 &nvCmd->auth_object);
312 return_try_again(r);
313 goto_if_error_reset_state(r, "read_finish failed", error_cleanup);
314
315 /* Initialize the esys object for the hierarchy. */
316 r = ifapi_initialize_object(context->esys, &nvCmd->auth_object);
317 goto_if_error_reset_state(r, "Initialize NV object", error_cleanup);
318
319 nvCmd->auth_object.handle
320 = miscNv->hierarchy;
321
322 /* Check if a policy is set for the NV index to be created. */
323 if (miscNv->policyInstance &&
324 strcmp(miscNv->policyInstance, "") != 0)
325 nvCmd->skip_policy_computation = false;
326 else
327 nvCmd->skip_policy_computation = true;
328 fallthrough;
329
330 statecase(context->state, NV_CREATE_CALCULATE_POLICY)
331 if (!nvCmd->skip_policy_computation) {
332 /* Calculate the policy as read for the keystore. */
333 r = ifapi_calculate_tree(context,
334 miscNv->policyInstance,
335 policy,
336 miscNv->public.nvPublic.nameAlg,
337 &context->policy.digest_idx,
338 &context->policy.hash_size);
339 return_try_again(r);
340
341 goto_if_error2(r, "Calculate policy tree %s", error_cleanup,
342 context->cmd.Key_Create.policyPath);
343
344 /* Store the calculated policy in the NV object */
345 *nvCmdPolicy = calloc(1,
346 sizeof(TPMS_POLICY));
347 goto_if_null(*nvCmdPolicy,
348 "Out of memory,", TSS2_FAPI_RC_MEMORY, error_cleanup);
349 **(nvCmdPolicy) = *policy;
350
351 authPolicy->size =
352 context->policy.hash_size;
353 memcpy(&authPolicy->buffer[0],
354 &policy->policyDigests.digests[context->policy.digest_idx].digest,
355 context->policy.hash_size);
356 LOGBLOB_TRACE(
357 &authPolicy->buffer[0],
358 context->policy.hash_size, "Create Key Policy");
359 }
360 fallthrough;
361
362 statecase(context->state, NV_CREATE_GET_INDEX)
363 r = ifapi_get_nv_start_index(nvCmd->nvPath,
364 &publicInfo->nvPublic.nvIndex);
365 goto_if_error_reset_state(r, "FAPI get handle index.", error_cleanup);
366
367 /* We are searching for a new free NV-index handle. */
368 r = ifapi_get_free_handle_async(context, &publicInfo->nvPublic.nvIndex);
369 goto_if_error_reset_state(r, "FAPI get handle index.", error_cleanup);
370 nvCmd->maxNvIndex = publicInfo->nvPublic.nvIndex + 100;
371
372 fallthrough;
373
374 statecase(context->state, NV_CREATE_FIND_INDEX)
375 r = ifapi_get_free_handle_finish(context, &publicInfo->nvPublic.nvIndex,
376 nvCmd->maxNvIndex);
377 return_try_again(r);
378 goto_if_error_reset_state(r, "FAPI get handle index.", error_cleanup);
379
380 /* Start a authorization session for the NV creation. */
381 context->primary_state = PRIMARY_INIT;
382 r = ifapi_get_sessions_async(context,
383 IFAPI_SESSION_GENEK | IFAPI_SESSION1,
384 0, 0);
385 goto_if_error_reset_state(r, "Create sessions", error_cleanup);
386 fallthrough;
387
388 statecase(context->state, NV_CREATE_WAIT_FOR_SESSION)
389 r = ifapi_get_sessions_finish(context, &context->profiles.default_profile,
390 context->profiles.default_profile.nameAlg);
391 return_try_again(r);
392 goto_if_error_reset_state(r, " FAPI create session", error_cleanup);
393
394
395 fallthrough;
396
397 statecase(context->state, NV_CREATE_AUTHORIZE_HIERARCHY)
398 /* Authorize with the storage hierarhcy / "owner" for NV creation. */
399 r = ifapi_authorize_object(context, &nvCmd->auth_object, &auth_session);
400 FAPI_SYNC(r, "Authorize hierarchy.", error_cleanup);
401
402 /* Create the NV Index. */
403 r = Esys_NV_DefineSpace_Async(context->esys,
404 hierarchy->handle,
405 auth_session,
406 ESYS_TR_NONE,
407 ESYS_TR_NONE,
408 auth,
409 publicInfo);
410 goto_if_error_reset_state(r, " Fapi_CreateNv_Async", error_cleanup);
411 fallthrough;
412
413 statecase(context->state, NV_CREATE_AUTH_SENT)
414 r = Esys_NV_DefineSpace_Finish(context->esys, &nvHandle);
415 return_try_again(r);
416
417 goto_if_error_reset_state(r, "FAPI CreateWithTemplate_Finish", error_cleanup);
418
419 /* Store whether the NV index requires a password. */
420 nvCmd->nv_object.handle = nvHandle;
421 if (nvCmd->auth.size > 0)
422 miscNv->with_auth = TPM2_YES;
423 else
424 miscNv->with_auth = TPM2_NO;
425
426 /* Perform esys serialization if necessary */
427 r = ifapi_esys_serialize_object(context->esys, &nvCmd->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 nvCmd->nvPath,
433 &nvCmd->nv_object);
434 goto_if_error_reset_state(r, "Could not open: %sh", error_cleanup,
435 nvCmd->nvPath);
436
437 fallthrough;
438
439 statecase(context->state, NV_CREATE_WRITE)
440 /* Finish writing the NV object to the key store */
441 r = ifapi_keystore_store_finish(&context->keystore, &context->io);
442 return_try_again(r);
443 return_if_error_reset_state(r, "write_finish failed");
444
445 break;
446
447 statecasedefault(context->state);
448 }
449
450 context->state = _FAPI_STATE_INIT;
451 LOG_DEBUG("success");
452 r = TSS2_RC_SUCCESS;
453
454 error_cleanup:
455 /* Cleanup any intermediate results and state stored in the context. */
456 ifapi_cleanup_ifapi_object(&nvCmd->nv_object);
457 ifapi_cleanup_ifapi_object(&nvCmd->auth_object);
458 ifapi_cleanup_ifapi_object(&context->createPrimary.pkey_object);
459 ifapi_cleanup_ifapi_object(context->loadKey.key_object);
460 ifapi_cleanup_ifapi_object(&context->loadKey.auth_object);
461 SAFE_FREE(miscNv->policyInstance);
462 SAFE_FREE(nvCmd->nvPath);
463 ifapi_session_clean(context);
464 LOG_TRACE("finished");
465 return r;
466 }
467