tpm2_session.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. /* SPDX-License-Identifier: BSD-3-Clause */
  2. #include <errno.h>
  3. #include <stdbool.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include "files.h"
  8. #include "log.h"
  9. #include "tpm2.h"
  10. #include "tpm2_session.h"
  11. struct tpm2_session_data {
  12. ESYS_TR key;
  13. ESYS_TR bind;
  14. TPM2_SE session_type;
  15. TPMT_SYM_DEF symmetric;
  16. TPMI_ALG_HASH auth_hash;
  17. TPM2B_NONCE nonce_caller;
  18. TPMA_SESSION attrs;
  19. TPM2B_AUTH auth_data;
  20. const char *path;
  21. };
  22. struct tpm2_session {
  23. tpm2_session_data* input;
  24. struct {
  25. ESYS_TR session_handle;
  26. } output;
  27. struct {
  28. char *path;
  29. ESYS_CONTEXT *ectx;
  30. bool is_final;
  31. } internal;
  32. };
  33. tpm2_session_data *tpm2_session_data_new(TPM2_SE type) {
  34. tpm2_session_data * d = calloc(1, sizeof(tpm2_session_data));
  35. if (d) {
  36. d->symmetric.algorithm = TPM2_ALG_NULL;
  37. d->key = ESYS_TR_NONE;
  38. d->bind = ESYS_TR_NONE;
  39. d->session_type = type;
  40. d->auth_hash = TPM2_ALG_SHA256;
  41. }
  42. return d;
  43. }
  44. void tpm2_session_set_key(tpm2_session_data *data, ESYS_TR key) {
  45. data->key = key;
  46. }
  47. void tpm2_session_set_attrs(tpm2_session_data *data, TPMA_SESSION attrs) {
  48. data->attrs = attrs;
  49. }
  50. void tpm2_session_set_auth_value(tpm2_session *session, TPM2B_AUTH *auth) {
  51. if (auth == NULL) {
  52. session->input->auth_data.size = 0;
  53. memset(session->input->auth_data.buffer, 0xBA,
  54. sizeof(session->input->auth_data.buffer));
  55. } else {
  56. memcpy(&session->input->auth_data, auth, sizeof(*auth));
  57. }
  58. }
  59. void tpm2_session_set_nonce_caller(tpm2_session_data *data, TPM2B_NONCE *nonce) {
  60. data->nonce_caller = *nonce;
  61. }
  62. void tpm2_session_set_bind(tpm2_session_data *data, ESYS_TR bind) {
  63. data->bind = bind;
  64. }
  65. void tpm2_session_set_type(tpm2_session_data *data, TPM2_SE type) {
  66. data->session_type = type;
  67. }
  68. void tpm2_session_set_symmetric(tpm2_session_data *data,
  69. TPMT_SYM_DEF *symmetric) {
  70. data->symmetric = *symmetric;
  71. }
  72. void tpm2_session_set_authhash(tpm2_session_data *data, TPMI_ALG_HASH auth_hash) {
  73. data->auth_hash = auth_hash;
  74. }
  75. void tpm2_session_set_path(tpm2_session_data *data, const char *path) {
  76. data->path = path;
  77. }
  78. TPMI_ALG_HASH tpm2_session_get_authhash(tpm2_session *session) {
  79. return session->input->auth_hash;
  80. }
  81. ESYS_TR tpm2_session_get_handle(tpm2_session *session) {
  82. return session->output.session_handle;
  83. }
  84. TPM2_SE tpm2_session_get_type(tpm2_session *session) {
  85. return session->input->session_type;
  86. }
  87. const TPM2B_AUTH *tpm2_session_get_auth_value(tpm2_session *session) {
  88. return &session->input->auth_data;
  89. }
  90. //
  91. // This is a wrapper function around the StartAuthSession command.
  92. // It performs the command, calculates the session key, and updates a
  93. // SESSION structure.
  94. //
  95. static tool_rc start_auth_session(tpm2_session *session) {
  96. tpm2_session_data *d = session->input;
  97. TPM2B_NONCE *nonce =
  98. session->input->nonce_caller.size > 0 ?
  99. &session->input->nonce_caller : NULL;
  100. tool_rc rc = tpm2_start_auth_session(session->internal.ectx, d->key,
  101. d->bind, nonce, d->session_type, &d->symmetric, d->auth_hash,
  102. &session->output.session_handle);
  103. if (rc != tool_rc_success) {
  104. return rc;
  105. }
  106. if (d->attrs) {
  107. rc = tpm2_sess_set_attributes(session->internal.ectx,
  108. session->output.session_handle, d->attrs, 0xff);
  109. if (rc != tool_rc_success) {
  110. tool_rc tmp_rc = tpm2_flush_context(session->internal.ectx,
  111. session->output.session_handle);
  112. UNUSED(tmp_rc);
  113. return rc;
  114. }
  115. }
  116. return tool_rc_success;
  117. }
  118. static void tpm2_session_free(tpm2_session **session) {
  119. tpm2_session *s = *session;
  120. if (s) {
  121. free(s->input);
  122. if (s->internal.path) {
  123. free(s->internal.path);
  124. }
  125. free(s);
  126. *session = NULL;
  127. }
  128. }
  129. tool_rc tpm2_session_open(ESYS_CONTEXT *context, tpm2_session_data *data,
  130. tpm2_session **session) {
  131. tpm2_session *s = calloc(1, sizeof(tpm2_session));
  132. if (!s) {
  133. free(data);
  134. LOG_ERR("oom");
  135. return tool_rc_general_error;
  136. }
  137. if (data->path) {
  138. s->internal.path = strdup(data->path);
  139. if (!s->internal.path) {
  140. LOG_ERR("oom");
  141. tpm2_session_free(&s);
  142. return tool_rc_general_error;
  143. }
  144. }
  145. s->input = data;
  146. s->internal.ectx = context;
  147. if (!context) {
  148. s->output.session_handle = ESYS_TR_PASSWORD;
  149. *session = s;
  150. return tool_rc_success;
  151. }
  152. tool_rc rc = start_auth_session(s);
  153. if (rc != tool_rc_success) {
  154. tpm2_session_free(&s);
  155. return rc;
  156. }
  157. *session = s;
  158. return tool_rc_success;
  159. }
  160. /* SESSION_VERSION 1 was used prior to the switch to ESAPI. As the types of
  161. * several of the tpm2_session_data object members have changed the version is
  162. * bumped.
  163. */
  164. #define SESSION_VERSION 2
  165. /*
  166. * Checks that two types are equal in size.
  167. *
  168. * It works by leveraging the fact that C does not allow negative array sizes.
  169. * If the sizes are equal, the boolean equality operator will return 1, thus
  170. * a subtraction of 1 yields 0, which is a legal array size in C. In the false
  171. * case (ie sizes not equal), 0 - 1 is -1, which will cause the compiler to
  172. * complain.
  173. */
  174. #define COMPILE_ASSERT_SIZE(a, b) \
  175. typedef char WRONG_SIZE_##a[(sizeof(a) == sizeof(b)) - 1]
  176. // We check that the TSS library does not change sizes unbeknownst to us.
  177. COMPILE_ASSERT_SIZE(ESYS_TR, UINT32);
  178. COMPILE_ASSERT_SIZE(TPMI_ALG_HASH, UINT16);
  179. COMPILE_ASSERT_SIZE(TPM2_SE, UINT8);
  180. tool_rc tpm2_session_restore(ESYS_CONTEXT *ctx, const char *path, bool is_final,
  181. tpm2_session **session) {
  182. tool_rc rc = tool_rc_general_error;
  183. tpm2_session *s = NULL;
  184. /*
  185. * Copy the string internally so callers need
  186. * not worry about it.
  187. */
  188. char *dup_path = strdup(path);
  189. if (!dup_path) {
  190. LOG_ERR("oom");
  191. return tool_rc_general_error;
  192. }
  193. FILE *f = fopen(dup_path, "rb");
  194. if (!f) {
  195. LOG_ERR("Could not open path \"%s\", due to error: \"%s\"", dup_path,
  196. strerror(errno));
  197. free(dup_path);
  198. return tool_rc_general_error;
  199. }
  200. uint32_t version;
  201. bool result = files_read_header(f, &version);
  202. TPM2_SE type;
  203. result = files_read_bytes(f, &type, sizeof(type));
  204. if (!result) {
  205. LOG_ERR("Could not read session type");
  206. goto out;
  207. }
  208. TPMI_ALG_HASH auth_hash;
  209. result = files_read_16(f, &auth_hash);
  210. if (!result) {
  211. LOG_ERR("Could not read session digest algorithm");
  212. goto out;
  213. }
  214. ESYS_TR handle;
  215. tool_rc tmp_rc = files_load_tpm_context_from_file(ctx, &handle, f);
  216. if (tmp_rc != tool_rc_success) {
  217. rc = tmp_rc;
  218. LOG_ERR("Could not load session context");
  219. goto out;
  220. }
  221. tpm2_session_data *d = tpm2_session_data_new(type);
  222. if (!d) {
  223. LOG_ERR("oom");
  224. goto out;
  225. }
  226. tpm2_session_set_authhash(d, auth_hash);
  227. tmp_rc = tpm2_session_open(NULL, d, &s);
  228. if (tmp_rc != tool_rc_success) {
  229. rc = tmp_rc;
  230. LOG_ERR("oom new session object");
  231. goto out;
  232. }
  233. s->output.session_handle = handle;
  234. s->internal.path = dup_path;
  235. s->internal.ectx = ctx;
  236. dup_path = NULL;
  237. TPMA_SESSION attrs = 0;
  238. if (ctx) {
  239. /* hack this in here, should be done when starting the session */
  240. tmp_rc = tpm2_sess_get_attributes(ctx, handle, &attrs);
  241. UNUSED(tmp_rc);
  242. }
  243. s->internal.is_final = is_final;
  244. *session = s;
  245. LOG_INFO("Restored session: ESYS_TR(0x%x) attrs(0x%x)", handle, attrs);
  246. rc = tool_rc_success;
  247. out:
  248. free(dup_path);
  249. if (f) {
  250. fclose(f);
  251. }
  252. return rc;
  253. }
  254. tool_rc tpm2_session_get_noncetpm(ESYS_CONTEXT *ectx, tpm2_session *s,
  255. TPM2B_NONCE **nonce_tpm) {
  256. ESYS_TR session_handle = tpm2_session_get_handle(s);
  257. return tpm2_sess_get_noncetpm(ectx, session_handle, nonce_tpm);
  258. }
  259. tool_rc tpm2_session_close(tpm2_session **s) {
  260. if (!*s) {
  261. return tool_rc_success;
  262. }
  263. /*
  264. * Do not back up:
  265. * - password sessions are implicit
  266. * - hmac sessions live the life of the tool
  267. */
  268. tool_rc rc = tool_rc_success;
  269. tpm2_session *session = *s;
  270. if (session->output.session_handle == ESYS_TR_PASSWORD) {
  271. goto out2;
  272. }
  273. const char *path = session->internal.path;
  274. FILE *session_file = path ? fopen(path, "w+b") : NULL;
  275. if (path && !session_file) {
  276. LOG_ERR("Could not open path \"%s\", due to error: \"%s\"", path,
  277. strerror(errno));
  278. rc = tool_rc_general_error;
  279. goto out;
  280. }
  281. bool flush = path ? session->internal.is_final : true;
  282. if (flush) {
  283. rc = tpm2_flush_context(session->internal.ectx,
  284. session->output.session_handle);
  285. /* done, use rc to indicate status */
  286. goto out;
  287. }
  288. /*
  289. * Now write the session_type, handle and auth hash data to disk
  290. */
  291. bool result = files_write_header(session_file, SESSION_VERSION);
  292. if (!result) {
  293. LOG_ERR("Could not write context file header");
  294. rc = tool_rc_general_error;
  295. goto out;
  296. }
  297. // UINT8 session type:
  298. TPM2_SE session_type = session->input->session_type;
  299. result = files_write_bytes(session_file, &session_type,
  300. sizeof(session_type));
  301. if (!result) {
  302. LOG_ERR("Could not write session type");
  303. rc = tool_rc_general_error;
  304. goto out;
  305. }
  306. // UINT16 - auth hash digest
  307. TPMI_ALG_HASH hash = tpm2_session_get_authhash(session);
  308. result = files_write_16(session_file, hash);
  309. if (!result) {
  310. LOG_ERR("Could not write auth hash");
  311. rc = tool_rc_general_error;
  312. goto out;
  313. }
  314. /*
  315. * Save session context at end of tpm2_session. With tabrmd support it
  316. * can be reloaded under certain circumstances.
  317. */
  318. ESYS_TR handle = tpm2_session_get_handle(session);
  319. LOG_INFO("Saved session: ESYS_TR(0x%x)", handle);
  320. rc = files_save_tpm_context_to_file(session->internal.ectx, handle,
  321. session_file);
  322. if (rc != tool_rc_success) {
  323. LOG_ERR("Could not write session context");
  324. /* done, free session resources and use rc to indicate status */
  325. }
  326. out:
  327. if (session_file) {
  328. fclose(session_file);
  329. }
  330. out2:
  331. tpm2_session_free(s);
  332. return rc;
  333. }
  334. tool_rc tpm2_session_restart(ESYS_CONTEXT *context, tpm2_session *s) {
  335. ESYS_TR handle = tpm2_session_get_handle(s);
  336. return tpm2_policy_restart(context, handle, ESYS_TR_NONE, ESYS_TR_NONE,
  337. ESYS_TR_NONE);
  338. }