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 <json-c/json_util.h>
12 #include <json-c/json_tokener.h>
13 #include <string.h>
14 
15 #include "ifapi_json_serialize.h"
16 #include "tss2_fapi.h"
17 #include "fapi_int.h"
18 #include "fapi_util.h"
19 #include "tss2_esys.h"
20 #define LOGMODULE fapi
21 #include "util/log.h"
22 #include "util/aux_util.h"
23 
24 typedef struct {
25     char *description;
26     TPMI_POLICYTYPE capability;
27     UINT32 property;
28     UINT32 max;
29 } IFAPI_INFO_CAP;
30 
31 #define CAP_IDX_PT_FIXED 9
32 
33 static IFAPI_INFO_CAP info_cap_tab[] = {
34     { "algorithms", TPM2_CAP_ALGS,  TPM2_ALG_FIRST, TPM2_MAX_CAP_ALGS},
35     { "handles-transient", TPM2_CAP_HANDLES, TPM2_TRANSIENT_FIRST, TPM2_MAX_CAP_HANDLES},
36     { "handles-persistent", TPM2_CAP_HANDLES, TPM2_PERSISTENT_FIRST, TPM2_MAX_CAP_HANDLES},
37     { "handles-permanent", TPM2_CAP_HANDLES, TPM2_PERMANENT_FIRST, TPM2_MAX_CAP_HANDLES},
38     { "handles-pcr", TPM2_CAP_HANDLES, TPM2_PCR_FIRST, TPM2_MAX_CAP_HANDLES},
39     { "handles-nv-index", TPM2_CAP_HANDLES, TPM2_NV_INDEX_FIRST, TPM2_MAX_CAP_HANDLES},
40     { "handles-loaded-session", TPM2_CAP_HANDLES, TPM2_LOADED_SESSION_FIRST, TPM2_MAX_CAP_HANDLES},
41     { "handles-action-session", TPM2_CAP_HANDLES, TPM2_ACTIVE_SESSION_FIRST, TPM2_MAX_CAP_HANDLES},
42     { "handles-saved-session", TPM2_CAP_HANDLES, TPM2_ACTIVE_SESSION_FIRST, TPM2_MAX_CAP_HANDLES},
43     { "properties-fixed", TPM2_CAP_TPM_PROPERTIES, TPM2_PT_FIXED, TPM2_MAX_TPM_PROPERTIES },
44     { "properties-variable", TPM2_CAP_TPM_PROPERTIES, TPM2_PT_VAR, TPM2_MAX_TPM_PROPERTIES },
45     { "commands", TPM2_CAP_COMMANDS, TPM2_CC_FIRST, TPM2_MAX_CAP_CC },
46     { "pp-commands", TPM2_CAP_PP_COMMANDS, TPM2_CC_FIRST, TPM2_MAX_CAP_CC },
47     { "audit-commands", TPM2_CAP_AUDIT_COMMANDS, TPM2_CC_FIRST, TPM2_MAX_CAP_CC },
48     { "pcrs", TPM2_CAP_PCRS, 0, TPM2_NUM_PCR_BANKS },
49     { "pcr-properties", TPM2_CAP_PCR_PROPERTIES, TPM2_PCR_FIRST, TPM2_MAX_PCR_PROPERTIES },
50     { "ecc-curves", TPM2_CAP_ECC_CURVES, 0, TPM2_MAX_ECC_CURVES },
51 };
52 
53 /** One-Call function for Fapi_GetInfo
54  *
55  * Returns a UTF-8 encoded string that identifies the versions of FAPI, TPM,
56  * configurations and other relevant information.
57  *
58  * @param[in,out] context The FAPI_CONTEXT
59  * @param[out] info The byte buffer for the information string
60  *
61  * @retval TSS2_RC_SUCCESS: if the function call was a success.
62  * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context or info is NULL.
63  * @retval TSS2_FAPI_RC_BAD_CONTEXT: if context corruption is detected.
64  * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous
65  *         operation already pending.
66  * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be saved.
67  * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for
68  *         internal operations or return parameters.
69  * @retval TSS2_FAPI_RC_NO_TPM if FAPI was initialized in no-TPM-mode via its
70  *         config file.
71  * @retval TSS2_FAPI_RC_TRY_AGAIN if an I/O operation is not finished yet and
72  *         this function needs to be called again.
73  * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
74  * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
75  *         the function.
76  * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
77  */
78 TSS2_RC
Fapi_GetInfo(FAPI_CONTEXT * context,char ** info)79 Fapi_GetInfo(
80     FAPI_CONTEXT *context,
81     char        **info)
82 {
83     LOG_TRACE("called for context:%p", context);
84 
85     TSS2_RC r, r2;
86 
87     /* Check for NULL parameters */
88     check_not_null(context);
89     check_not_null(info);
90 
91     /* Check whether TCTI and ESYS are initialized */
92     return_if_null(context->esys, "Command can't be executed in none TPM mode.",
93                    TSS2_FAPI_RC_NO_TPM);
94 
95     /* If the async state automata of FAPI shall be tested, then we must not set
96        the timeouts of ESYS to blocking mode.
97        During testing, the mssim tcti will ensure multiple re-invocations.
98        Usually however the synchronous invocations of FAPI shall instruct ESYS
99        to block until a result is available. */
100 #ifndef TEST_FAPI_ASYNC
101     r = Esys_SetTimeout(context->esys, TSS2_TCTI_TIMEOUT_BLOCK);
102     return_if_error_reset_state(r, "Set Timeout to blocking");
103 #endif /* TEST_FAPI_ASYNC */
104 
105     r = Fapi_GetInfo_Async(context);
106     return_if_error_reset_state(r, "GetTPMInfo");
107 
108     do {
109         /* We wait for file I/O to be ready if the FAPI state automata
110            are in a file I/O state. */
111         r = ifapi_io_poll(&context->io);
112         return_if_error(r, "Something went wrong with IO polling");
113 
114         /* Repeatedly call the finish function, until FAPI has transitioned
115            through all execution stages / states of this invocation. */
116         r = Fapi_GetInfo_Finish(context, info);
117     } while (base_rc(r) == TSS2_BASE_RC_TRY_AGAIN);
118 
119     /* Reset the ESYS timeout to non-blocking, immediate response. */
120     r2 = Esys_SetTimeout(context->esys, 0);
121     return_if_error(r2, "Set Timeout to non-blocking");
122 
123     return_if_error_reset_state(r, "GetTPMInfo");
124 
125     LOG_TRACE("finished");
126     return TSS2_RC_SUCCESS;
127 }
128 
129 /** Asynchronous function for Fapi_GetInfo
130  *
131  * Returns a UTF-8 encoded string that identifies the versions of FAPI, TPM,
132  * configurations and other relevant information.
133  *
134  * Call Fapi_GetInfo_Finish to finish the execution of this command.
135  *
136  * @param[in,out] context The FAPI_CONTEXT
137  *
138  * @retval TSS2_RC_SUCCESS: if the function call was a success.
139  * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context is NULL.
140  * @retval TSS2_FAPI_RC_BAD_CONTEXT: if context corruption is detected.
141  * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous
142  *         operation already pending.
143  * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be saved.
144  * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for
145  *         internal operations or return parameters.
146  * @retval TSS2_FAPI_RC_NO_TPM if FAPI was initialized in no-TPM-mode via its
147  *         config file.
148  */
149 TSS2_RC
Fapi_GetInfo_Async(FAPI_CONTEXT * context)150 Fapi_GetInfo_Async(
151     FAPI_CONTEXT *context)
152 {
153     LOG_TRACE("called for context:%p", context);
154 
155     TSS2_RC r;
156 
157     /* Check for NULL parameters */
158     check_not_null(context);
159 
160     /* Helpful alias pointers */
161     IFAPI_GetInfo * command = &context->cmd.GetInfo;
162 
163     /* Reset all context-internal session state information. */
164     r = ifapi_session_init(context);
165     return_if_error(r, "Initialize GetInfo");
166 
167     memset(command, 0, sizeof(IFAPI_GetInfo));
168     r = ifapi_capability_init(context);
169     return_if_error(r, "Capability init");
170 
171     /* Initialize the context state for this operation. */
172     command->idx_info_cap = 0;
173     context->state = GET_INFO_GET_CAP;
174 
175     LOG_TRACE("finished");
176     return TSS2_RC_SUCCESS;
177 }
178 
179 /** Asynchronous finish function for Fapi_GetInfo
180  *
181  * This function should be called after a previous Fapi_GetInfo_Async.
182  *
183  * @param[in,out] context The FAPI_CONTEXT
184  * @param[out] info The byte buffer for the information string
185  *
186  * @retval TSS2_RC_SUCCESS: if the function call was a success.
187  * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context or info is NULL.
188  * @retval TSS2_FAPI_RC_BAD_CONTEXT: if context corruption is detected.
189  * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous
190  *         operation already pending.
191  * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be saved.
192  * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for
193  *         internal operations or return parameters.
194  * @retval TSS2_FAPI_RC_TRY_AGAIN: if the asynchronous operation is not yet
195  *         complete. Call this function again later.
196  * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
197  * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
198  *         the function.
199  * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
200  */
201 TSS2_RC
Fapi_GetInfo_Finish(FAPI_CONTEXT * context,char ** info)202 Fapi_GetInfo_Finish(
203     FAPI_CONTEXT *context,
204     char        **info)
205 {
206     LOG_TRACE("called for context:%p", context);
207 
208     TSS2_RC r;
209     json_object *jso = NULL;
210     size_t capIdx, i;
211 
212     /* Check for NULL parameters */
213     check_not_null(context);
214     check_not_null(info);
215 
216     /* Helpful alias pointers */
217     IFAPI_GetInfo * command = &context->cmd.GetInfo;
218     IFAPI_INFO *infoObj = &command->info_obj;
219     TPMS_CAPABILITY_DATA *capabilityData = NULL;
220 
221     switch (context->state) {
222     case GET_INFO_GET_CAP:
223         /* Initialize the property for the first ESAPI call */
224         command->property
225             = info_cap_tab[command->idx_info_cap].property;
226         fallthrough;
227 
228     case GET_INFO_GET_CAP_MORE:
229         /* This state is a helper used from fapi_util.c */
230         fallthrough;
231 
232     case GET_INFO_WAIT_FOR_CAP:
233         /* State will be set by sub routine */
234         capIdx = command->idx_info_cap;
235         r = ifapi_capability_get(context,
236                                  info_cap_tab[capIdx].capability,
237                                  info_cap_tab[capIdx].max,
238                                  &capabilityData);
239         return_try_again(r);
240         goto_if_error(r, "Get capability", cleanup);
241 
242         if (info_cap_tab[capIdx].capability == TPM2_CAP_TPM_PROPERTIES &&
243             info_cap_tab[capIdx].property == TPM2_PT_FIXED) {
244             /* Adapt count to number of fixed properties. */
245             for (i = 0; i <  capabilityData->data.tpmProperties.count; i++) {
246                 /* TPM2_PT_MODES is the last fixed property. */
247                 if (capabilityData->data.tpmProperties.tpmProperty[i].property ==  TPM2_PT_MODES) {
248                     capabilityData->data.tpmProperties.count = i + 1;
249                     break;
250                 }
251             }
252         }
253 
254         infoObj->cap[capIdx].description = info_cap_tab[capIdx].description;
255         infoObj->cap[capIdx].capability = capabilityData;
256         command->property_count = 0;
257         command->idx_info_cap += 1;
258         if (command->idx_info_cap <  sizeof(info_cap_tab)
259                 / sizeof(info_cap_tab[0])) {
260             /* Not all capabilities have been collected */
261             context->state = GET_INFO_GET_CAP;
262             return TSS2_FAPI_RC_TRY_AGAIN;
263         }
264 
265         infoObj->fapi_version = PACKAGE_STRING;
266         infoObj->fapi_config = context->config;
267 
268         /* Serialize the information. */
269         r = ifapi_json_IFAPI_INFO_serialize(infoObj, &jso);
270         goto_if_error(r, "Error serialize info object", cleanup);
271 
272         /* Duplicate the information to be returned to the caller. */
273 #ifdef JSON_C_TO_STRING_NOSLASHESCAPE
274         *info = strdup(json_object_to_json_string_ext(jso, JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_NOSLASHESCAPE));
275 #else
276          *info = strdup(json_object_to_json_string_ext(jso, JSON_C_TO_STRING_PRETTY));
277 #endif
278         goto_if_null2(*info, "Out of memory.", r, TSS2_FAPI_RC_MEMORY, cleanup);
279 
280         context->state = _FAPI_STATE_INIT;
281         r = TSS2_RC_SUCCESS;
282         break;
283 
284     statecasedefault(context->state);
285     }
286 
287 cleanup:
288     /* Cleanup any intermediate results and state stored in the context. */
289     json_object_put(jso);
290     for (capIdx = 0; capIdx < IFAPI_MAX_CAP_INFO; capIdx++) {
291         SAFE_FREE(infoObj->cap[capIdx].capability);
292     }
293     LOG_TRACE("finished");
294     return r;
295 }
296