tpm2_attr_util.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646
  1. /* SPDX-License-Identifier: BSD-3-Clause */
  2. #include <stdbool.h>
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include "log.h"
  7. #include "tpm2_attr_util.h"
  8. #define dispatch_no_arg_add(x) \
  9. { .name = str(x), .callback=(action)x, .width = 1 }
  10. #define dispatch_arg_add(x, w) \
  11. { .name = str(x), .callback=(action)x, .width = w }
  12. #define dispatch_reserved(pos) \
  13. { .name = "<reserved("xstr(pos)")>", .callback=NULL, .width = 1 }
  14. typedef enum dispatch_error dispatch_error;
  15. enum dispatch_error {
  16. dispatch_ok = 0, dispatch_err, dispatch_no_match,
  17. };
  18. typedef bool (*action)(void *obj, char *arg);
  19. typedef struct dispatch_table dispatch_table;
  20. struct dispatch_table {
  21. char *name;
  22. action callback;
  23. unsigned width; /* the width of the field, CANNOT be 0 */
  24. };
  25. static bool authread(TPMA_NV *nv, char *arg) {
  26. UNUSED(arg);
  27. *nv |= TPMA_NV_AUTHREAD;
  28. return true;
  29. }
  30. static bool authwrite(TPMA_NV *nv, char *arg) {
  31. UNUSED(arg);
  32. *nv |= TPMA_NV_AUTHWRITE;
  33. return true;
  34. }
  35. static bool clear_stclear(TPMA_NV *nv, char *arg) {
  36. UNUSED(arg);
  37. *nv |= TPMA_NV_CLEAR_STCLEAR;
  38. return true;
  39. }
  40. static bool globallock(TPMA_NV *nv, char *arg) {
  41. UNUSED(arg);
  42. *nv |= TPMA_NV_GLOBALLOCK;
  43. return true;
  44. }
  45. static bool no_da(TPMA_NV *nv, char *arg) {
  46. UNUSED(arg);
  47. *nv |= TPMA_NV_NO_DA;
  48. return true;
  49. }
  50. static bool orderly(TPMA_NV *nv, char *arg) {
  51. UNUSED(arg);
  52. *nv |= TPMA_NV_ORDERLY;
  53. return true;
  54. }
  55. static bool ownerread(TPMA_NV *nv, char *arg) {
  56. UNUSED(arg);
  57. *nv |= TPMA_NV_OWNERREAD;
  58. return true;
  59. }
  60. static bool ownerwrite(TPMA_NV *nv, char *arg) {
  61. UNUSED(arg);
  62. *nv |= TPMA_NV_OWNERWRITE;
  63. return true;
  64. }
  65. static bool platformcreate(TPMA_NV *nv, char *arg) {
  66. UNUSED(arg);
  67. *nv |= TPMA_NV_PLATFORMCREATE;
  68. return true;
  69. }
  70. static bool policyread(TPMA_NV *nv, char *arg) {
  71. UNUSED(arg);
  72. *nv |= TPMA_NV_POLICYREAD;
  73. return true;
  74. }
  75. static bool policywrite(TPMA_NV *nv, char *arg) {
  76. UNUSED(arg);
  77. *nv |= TPMA_NV_POLICYWRITE;
  78. return true;
  79. }
  80. static bool policydelete(TPMA_NV *nv, char *arg) {
  81. UNUSED(arg);
  82. *nv |= TPMA_NV_POLICY_DELETE;
  83. return true;
  84. }
  85. static bool ppread(TPMA_NV *nv, char *arg) {
  86. UNUSED(arg);
  87. *nv |= TPMA_NV_PPREAD;
  88. return true;
  89. }
  90. static bool ppwrite(TPMA_NV *nv, char *arg) {
  91. UNUSED(arg);
  92. *nv |= TPMA_NV_PPWRITE;
  93. return true;
  94. }
  95. static bool readlocked(TPMA_NV *nv, char *arg) {
  96. UNUSED(arg);
  97. *nv |= TPMA_NV_READLOCKED;
  98. return true;
  99. }
  100. static bool read_stclear(TPMA_NV *nv, char *arg) {
  101. UNUSED(arg);
  102. *nv |= TPMA_NV_READ_STCLEAR;
  103. return true;
  104. }
  105. static bool writeall(TPMA_NV *nv, char *arg) {
  106. UNUSED(arg);
  107. *nv |= TPMA_NV_WRITEALL;
  108. return true;
  109. }
  110. static bool writedefine(TPMA_NV *nv, char *arg) {
  111. UNUSED(arg);
  112. *nv |= TPMA_NV_WRITEDEFINE;
  113. return true;
  114. }
  115. static bool writelocked(TPMA_NV *nv, char *arg) {
  116. UNUSED(arg);
  117. *nv |= TPMA_NV_WRITELOCKED;
  118. return true;
  119. }
  120. static bool write_stclear(TPMA_NV *nv, char *arg) {
  121. UNUSED(arg);
  122. *nv |= TPMA_NV_WRITE_STCLEAR;
  123. return true;
  124. }
  125. static bool written(TPMA_NV *nv, char *arg) {
  126. UNUSED(arg);
  127. *nv |= TPMA_NV_WRITTEN;
  128. return true;
  129. }
  130. static bool lookup_nt_friendly_name(const char *arg, uint16_t *type) {
  131. if (!strcmp(arg, "ordinary")) {
  132. // Nothing to do
  133. } else if (!strcmp(arg, "counter")) {
  134. *type = TPM2_NT_COUNTER;
  135. } else if (!strcmp(arg, "bits")) {
  136. *type = TPM2_NT_BITS;
  137. } else if (!strcmp(arg, "extend")) {
  138. *type = TPM2_NT_EXTEND;
  139. } else if (!strcmp(arg, "pinfail")) {
  140. *type = TPM2_NT_PIN_FAIL;
  141. } else if (!strcmp(arg, "pinpass")) {
  142. *type = TPM2_NT_PIN_PASS;
  143. } else {
  144. LOG_ERR("Unknown NT type specifier, got: \"%s\"", arg);
  145. return false;
  146. }
  147. return true;
  148. }
  149. static bool nt(TPMA_NV *nv, char *arg) {
  150. uint16_t value;
  151. bool result = tpm2_util_string_to_uint16(arg, &value);
  152. if (!result) {
  153. result = lookup_nt_friendly_name(arg, &value);
  154. if (!result) {
  155. LOG_ERR("Could not convert NT field, got \"%s\"."
  156. "Expected a number or friendly name", arg);
  157. return false;
  158. }
  159. }
  160. /* nt space is 4 bits, so max of 15 */
  161. if (value > 0x0F) {
  162. LOG_ERR("Field TPM_NT of type TPMA_NV is only 4 bits,"
  163. "value \"%s\" to big!", arg);
  164. return false;
  165. }
  166. *nv &= ~TPMA_NV_TPM2_NT_MASK;
  167. *nv |= value << 4;
  168. return true;
  169. }
  170. /*
  171. * The order of this table must be in order with the bit defines in table 204:
  172. * https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-01.38.pdf
  173. *
  174. * This table is in bitfield order, thus the index of a bit set in a TPMA_NV
  175. * can be used to lookup the name.
  176. *
  177. * if not the logic in tpm2_nv_util_strtoattr would need to change!
  178. */
  179. static dispatch_table nv_attr_table[] = { // Bit Index
  180. dispatch_no_arg_add(ppwrite), // 0
  181. dispatch_no_arg_add(ownerwrite), // 1
  182. dispatch_no_arg_add(authwrite), // 2
  183. dispatch_no_arg_add(policywrite), // 3
  184. dispatch_arg_add(nt, 4), // 4
  185. dispatch_arg_add(nt, 3), // 5
  186. dispatch_arg_add(nt, 2), // 6
  187. dispatch_arg_add(nt, 1), // 7
  188. dispatch_reserved(8), // 8
  189. dispatch_reserved(9), // 9
  190. dispatch_no_arg_add(policydelete), // 10
  191. dispatch_no_arg_add(writelocked), // 11
  192. dispatch_no_arg_add(writeall), // 12
  193. dispatch_no_arg_add(writedefine), // 13
  194. dispatch_no_arg_add(write_stclear), // 14
  195. dispatch_no_arg_add(globallock), // 15
  196. dispatch_no_arg_add(ppread), // 16
  197. dispatch_no_arg_add(ownerread), // 17
  198. dispatch_no_arg_add(authread), // 18
  199. dispatch_no_arg_add(policyread), // 19
  200. dispatch_reserved(20), // 20
  201. dispatch_reserved(21), // 21
  202. dispatch_reserved(22), // 22
  203. dispatch_reserved(23), // 23
  204. dispatch_reserved(24), // 24
  205. dispatch_no_arg_add(no_da), // 25
  206. dispatch_no_arg_add(orderly), // 26
  207. dispatch_no_arg_add(clear_stclear), // 27
  208. dispatch_no_arg_add(readlocked), // 28
  209. dispatch_no_arg_add(written), // 29
  210. dispatch_no_arg_add(platformcreate), // 30
  211. dispatch_no_arg_add(read_stclear), // 31
  212. };
  213. static bool fixedtpm(TPMA_OBJECT *obj, char *arg) {
  214. UNUSED(arg);
  215. *obj |= TPMA_OBJECT_FIXEDTPM;
  216. return true;
  217. }
  218. static bool stclear(TPMA_OBJECT *obj, char *arg) {
  219. UNUSED(arg);
  220. *obj |= TPMA_OBJECT_STCLEAR;
  221. return true;
  222. }
  223. static bool fixedparent(TPMA_OBJECT *obj, char *arg) {
  224. UNUSED(arg);
  225. *obj |= TPMA_OBJECT_FIXEDPARENT;
  226. return true;
  227. }
  228. static bool sensitivedataorigin(TPMA_OBJECT *obj, char *arg) {
  229. UNUSED(arg);
  230. *obj |= TPMA_OBJECT_SENSITIVEDATAORIGIN;
  231. return true;
  232. }
  233. static bool userwithauth(TPMA_OBJECT *obj, char *arg) {
  234. UNUSED(arg);
  235. *obj |= TPMA_OBJECT_USERWITHAUTH;
  236. return true;
  237. }
  238. static bool adminwithpolicy(TPMA_OBJECT *obj, char *arg) {
  239. UNUSED(arg);
  240. *obj |= TPMA_OBJECT_ADMINWITHPOLICY;
  241. return true;
  242. }
  243. static bool noda(TPMA_OBJECT *obj, char *arg) {
  244. UNUSED(arg);
  245. *obj |= TPMA_OBJECT_NODA;
  246. return true;
  247. }
  248. static bool encryptedduplication(TPMA_OBJECT *obj, char *arg) {
  249. UNUSED(arg);
  250. *obj |= TPMA_OBJECT_ENCRYPTEDDUPLICATION;
  251. return true;
  252. }
  253. static bool restricted(TPMA_OBJECT *obj, char *arg) {
  254. UNUSED(arg);
  255. *obj |= TPMA_OBJECT_RESTRICTED;
  256. return true;
  257. }
  258. static bool decrypt(TPMA_OBJECT *obj, char *arg) {
  259. UNUSED(arg);
  260. *obj |= TPMA_OBJECT_DECRYPT;
  261. return true;
  262. }
  263. static bool sign(TPMA_OBJECT *obj, char *arg) {
  264. UNUSED(arg);
  265. *obj |= TPMA_OBJECT_SIGN_ENCRYPT;
  266. return true;
  267. }
  268. static dispatch_table obj_attr_table[] = { // Bit Index
  269. dispatch_reserved(0), // 0
  270. dispatch_no_arg_add(fixedtpm), // 1
  271. dispatch_no_arg_add(stclear), // 2
  272. dispatch_reserved(3), // 3
  273. dispatch_no_arg_add(fixedparent), // 4
  274. dispatch_no_arg_add(sensitivedataorigin), // 5
  275. dispatch_no_arg_add(userwithauth), // 6
  276. dispatch_no_arg_add(adminwithpolicy), // 7
  277. dispatch_reserved(8), // 8
  278. dispatch_reserved(9), // 9
  279. dispatch_no_arg_add(noda), // 10
  280. dispatch_no_arg_add(encryptedduplication), // 11
  281. dispatch_reserved(12), // 12
  282. dispatch_reserved(13), // 13
  283. dispatch_reserved(14), // 14
  284. dispatch_reserved(15), // 15
  285. dispatch_no_arg_add(restricted), // 16
  286. dispatch_no_arg_add(decrypt), // 17
  287. dispatch_no_arg_add(sign), // 18
  288. dispatch_reserved(19), // 19
  289. dispatch_reserved(20), // 20
  290. dispatch_reserved(21), // 21
  291. dispatch_reserved(22), // 22
  292. dispatch_reserved(23), // 23
  293. dispatch_reserved(24), // 24
  294. dispatch_reserved(25), // 25
  295. dispatch_reserved(26), // 26
  296. dispatch_reserved(27), // 27
  297. dispatch_reserved(28), // 28
  298. dispatch_reserved(29), // 29
  299. dispatch_reserved(30), // 30
  300. dispatch_reserved(31), // 31
  301. };
  302. static bool token_match(const char *name, const char *token, bool has_arg,
  303. char **sep) {
  304. /* if it has an argument, we expect a separator */
  305. size_t match_len = strlen(token);
  306. if (has_arg) {
  307. *sep = strchr(token, '=');
  308. if (*sep) {
  309. match_len = *sep - token;
  310. }
  311. }
  312. return !strncmp(name, token, match_len);
  313. }
  314. static dispatch_error handle_dispatch(dispatch_table *d, char *token,
  315. TPMA_NV *nvattrs) {
  316. char *name = d->name;
  317. action cb = d->callback;
  318. bool has_arg = d->width > 1;
  319. /* if no callback, then its a reserved block, just skip it */
  320. if (!cb) {
  321. return dispatch_no_match;
  322. }
  323. char *sep = NULL;
  324. bool match = token_match(name, token, has_arg, &sep);
  325. if (!match) {
  326. return dispatch_no_match;
  327. }
  328. /*
  329. * If it has an argument, match should have found the separator.
  330. */
  331. char *arg = NULL;
  332. if (has_arg) {
  333. if (!sep) {
  334. LOG_ERR("Expected argument for \"%s\", got none.", token);
  335. return dispatch_err;
  336. }
  337. /* split token on = */
  338. *sep = '\0';
  339. sep++;
  340. if (!*sep) {
  341. LOG_ERR("Expected argument for \"%s\", got none.", token);
  342. return dispatch_err;
  343. }
  344. /* valid argument string, assign */
  345. arg = sep;
  346. }
  347. bool result = cb(nvattrs, arg);
  348. return result ? dispatch_ok : dispatch_err;
  349. }
  350. static bool common_strtoattr(char *attribute_list, void *attrs,
  351. dispatch_table *table, size_t size) {
  352. char *token;
  353. char *save;
  354. /*
  355. * This check is solely to prevent GCC from complaining on:
  356. * error: ‘attribute_list’ may be used uninitialized in this function [-Werror=maybe-uninitialized]
  357. * Might as well check nvattrs as well.
  358. */
  359. if (!attribute_list || !attrs) {
  360. LOG_ERR("attribute list or attributes structure is NULL");
  361. return false;
  362. }
  363. while ((token = strtok_r(attribute_list, "|", &save))) {
  364. attribute_list = NULL;
  365. bool did_dispatch = false;
  366. size_t i;
  367. for (i = 0; i < size; i++) {
  368. dispatch_table *d = &table[i];
  369. dispatch_error err = handle_dispatch(d, token, attrs);
  370. if (err == dispatch_ok) {
  371. did_dispatch = true;
  372. break;
  373. } else if (err == dispatch_err) {
  374. return false;
  375. }
  376. /* dispatch_no_match --> keep looking */
  377. }
  378. /* did we dispatch?, If not log error and return */
  379. if (!did_dispatch) {
  380. char *tmp = strchr(token, '=');
  381. if (tmp) {
  382. *tmp = '\0';
  383. }
  384. LOG_ERR("Unknown token: \"%s\" found.", token);
  385. return false;
  386. }
  387. }
  388. return true;
  389. }
  390. bool tpm2_attr_util_nv_strtoattr(char *attribute_list, TPMA_NV *nvattrs) {
  391. memset(nvattrs, 0, sizeof(*nvattrs));
  392. return common_strtoattr(attribute_list, nvattrs, nv_attr_table,
  393. ARRAY_LEN(nv_attr_table));
  394. }
  395. bool tpm2_attr_util_obj_strtoattr(char *attribute_list, TPMA_OBJECT *objattrs) {
  396. memset(objattrs, 0, sizeof(*objattrs));
  397. return common_strtoattr(attribute_list, objattrs, obj_attr_table,
  398. ARRAY_LEN(obj_attr_table));
  399. }
  400. static UINT8 find_first_set(UINT32 bits) {
  401. UINT8 n = 0;
  402. if (!bits) {
  403. return n;
  404. }
  405. if (!(bits & 0x0000FFFF)) {
  406. n += 16;
  407. bits >>= 16;
  408. }
  409. if (!(bits & 0x000000FF)) {
  410. n += 8;
  411. bits >>= 8;
  412. }
  413. if (!(bits & 0x0000000F)) {
  414. n += 4;
  415. bits >>= 4;
  416. }
  417. if (!(bits & 0x00000003)) {
  418. n += 2;
  419. bits >>= 2;
  420. }
  421. if (!(bits & 0x00000001))
  422. n += 1;
  423. return n;
  424. }
  425. static char *tpm2_attr_util_common_attrtostr(UINT32 attrs,
  426. dispatch_table *table, size_t size) {
  427. if (attrs == 0) {
  428. return strdup("<none>");
  429. }
  430. /*
  431. * Get how many bits are set in the attributes and then find the longest
  432. * "name".
  433. *
  434. * pop_cnt * max_name_len + pop_cnt - 1 (for the | separators) + 4
  435. * (for nv field equals in hex) + 1 for null byte.
  436. *
  437. * This will provide us an ample buffer size for generating the string
  438. * in without having to constantly realloc.
  439. */
  440. UINT32 pop_cnt = tpm2_util_pop_count(attrs);
  441. size_t i;
  442. size_t max_name_len = 0;
  443. for (i = 0; i < size; i++) {
  444. dispatch_table *d = &table[i];
  445. size_t name_len = strlen(d->name);
  446. max_name_len = name_len > max_name_len ? name_len : max_name_len;
  447. }
  448. size_t length = pop_cnt * max_name_len + pop_cnt - 1 + 3;
  449. char *str = calloc(length, 1);
  450. if (!str) {
  451. return NULL;
  452. }
  453. size_t string_index = 0;
  454. /*
  455. * Start at the lowest, first bit set, index into the array,
  456. * grab the data needed, and move on.
  457. */
  458. while (attrs) {
  459. UINT8 bit_index = find_first_set(attrs);
  460. dispatch_table *d = &table[bit_index];
  461. const char *name = d->name;
  462. unsigned w = d->width;
  463. /* current position and size left of the string */
  464. char *s = &str[string_index];
  465. size_t left = length - string_index;
  466. /* this is a mask that is field width wide */
  467. UINT8 mask = ((UINT32) 1 << w) - 1;
  468. /* get the value in the field before clearing attrs out */
  469. UINT8 field_values = (attrs & mask << bit_index) >> bit_index;
  470. /*
  471. * turn off the parsed bit(s) index, using width to turn off everything in a
  472. * field
  473. */
  474. attrs &= ~(mask << bit_index);
  475. int n;
  476. /*
  477. * if the callback is NULL, we are either in a field middle or reserved
  478. * section which is weird, just add the name in. In the case of being
  479. * in the middle of the field, we will add a bunch of errors to the string,
  480. * but it would be better to attempt to show all the data in string form,
  481. * rather than bail.
  482. *
  483. * Fields are either 1 or > 1.
  484. */
  485. if (w == 1) {
  486. n = snprintf(s, left, attrs ? "%s|" : "%s", name);
  487. } else {
  488. /* deal with the field */
  489. n = snprintf(s, left, attrs ? "%s=0x%X|" : "%s=0x%X", name,
  490. field_values);
  491. }
  492. /* check if there was enough space left in s */
  493. if ((n < 0) || ((unsigned) n >= left)) {
  494. break;
  495. }
  496. string_index += n;
  497. }
  498. return str;
  499. }
  500. char *tpm2_attr_util_nv_attrtostr(TPMA_NV nvattrs) {
  501. return tpm2_attr_util_common_attrtostr(nvattrs, nv_attr_table,
  502. ARRAY_LEN(nv_attr_table));
  503. }
  504. char *tpm2_attr_util_obj_attrtostr(TPMA_OBJECT objattrs) {
  505. return tpm2_attr_util_common_attrtostr(objattrs, obj_attr_table,
  506. ARRAY_LEN(obj_attr_table));
  507. }
  508. bool tpm2_attr_util_obj_from_optarg(char *argvalue, TPMA_OBJECT *objattrs) {
  509. bool res = tpm2_util_string_to_uint32(argvalue, objattrs);
  510. if (!res) {
  511. res = tpm2_attr_util_obj_strtoattr(argvalue, objattrs);
  512. }
  513. return res;
  514. }