tpm2_options.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  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 <ctype.h>
  8. #include <fcntl.h>
  9. #include <libgen.h>
  10. #include <unistd.h>
  11. #include <tss2/tss2_tctildr.h>
  12. #include <sys/types.h>
  13. #include <sys/wait.h>
  14. #include <sys/stat.h>
  15. #include "config.h"
  16. #include "log.h"
  17. #include "tpm2_options.h"
  18. #ifndef VERSION
  19. #warning "VERSION Not known at compile time, not embedding..."
  20. #define VERSION "UNKNOWN"
  21. #endif
  22. #define TPM2TOOLS_ENV_TCTI "TPM2TOOLS_TCTI"
  23. #define TPM2TOOLS_ENV_ENABLE_ERRATA "TPM2TOOLS_ENABLE_ERRATA"
  24. tpm2_options *tpm2_options_new(const char *short_opts, size_t len,
  25. const struct option *long_opts, tpm2_option_handler on_opt,
  26. tpm2_arg_handler on_arg, uint32_t flags) {
  27. tpm2_options *opts = calloc(1, sizeof(*opts) + (sizeof(*long_opts) * len));
  28. if (!opts) {
  29. LOG_ERR("oom");
  30. return NULL;
  31. }
  32. /*
  33. * On NULL, just make it a zero length string so we don't have to keep
  34. * checking it for NULL.
  35. */
  36. if (!short_opts) {
  37. short_opts = "";
  38. }
  39. opts->short_opts = strdup(short_opts);
  40. if (!opts->short_opts) {
  41. LOG_ERR("oom");
  42. free(opts);
  43. return NULL;
  44. }
  45. opts->callbacks.on_opt = on_opt;
  46. opts->callbacks.on_arg = on_arg;
  47. opts->len = len;
  48. opts->flags = flags;
  49. memcpy(opts->long_opts, long_opts, len * sizeof(*long_opts));
  50. return opts;
  51. }
  52. bool tpm2_options_cat(tpm2_options **dest, tpm2_options *src) {
  53. tpm2_options *d = *dest;
  54. /* move the nested char * pointer first */
  55. size_t opts_len = strlen(d->short_opts) + strlen(src->short_opts) + 1;
  56. char *tmp_short = realloc(d->short_opts, opts_len);
  57. if (!tmp_short) {
  58. LOG_ERR("oom");
  59. return false;
  60. }
  61. strcat(tmp_short, src->short_opts);
  62. d->short_opts = tmp_short;
  63. /* now move the enclosing structure */
  64. size_t long_opts_len = d->len + src->len;
  65. /* +1 for a terminating NULL at the end of options array for getopt_long */
  66. tpm2_options *tmp = realloc(d,
  67. sizeof(*d) + ((long_opts_len + 1) * sizeof(d->long_opts[0])));
  68. if (!tmp) {
  69. LOG_ERR("oom");
  70. return false;
  71. }
  72. *dest = d = tmp;
  73. d->callbacks.on_arg = src->callbacks.on_arg;
  74. d->callbacks.on_opt = src->callbacks.on_opt;
  75. d->flags = src->flags;
  76. memcpy(&d->long_opts[d->len], src->long_opts,
  77. src->len * sizeof(src->long_opts[0]));
  78. /* length must be updated post memcpy as we need d->len to be the original offset */
  79. d->len = long_opts_len;
  80. /* NULL term for getopt_long */
  81. memset(&d->long_opts[d->len], 0, sizeof(d->long_opts[0]));
  82. return true;
  83. }
  84. void tpm2_options_free(tpm2_options *opts) {
  85. if (!opts) {
  86. return;
  87. }
  88. free(opts->short_opts);
  89. free(opts);
  90. }
  91. static bool execute_man(char *prog_name, bool show_errors) {
  92. pid_t pid;
  93. int status;
  94. if ((pid = fork()) < 0) {
  95. LOG_ERR("Could not fork process to execute man, error: %s",
  96. strerror(errno));
  97. return false;
  98. }
  99. #define MAX_TOOL_NAME_LEN 64
  100. if (pid == 0) {
  101. if (!show_errors) {
  102. /* redirect manpager errors to stderr */
  103. int fd = open("/dev/null", O_WRONLY);
  104. if (fd < 0) {
  105. LOG_ERR("Could not open /dev/null");
  106. return false;
  107. }
  108. dup2(fd, 2);
  109. close(fd);
  110. }
  111. const char *manpage = basename(prog_name);
  112. if (!strcmp(manpage, "tpm2")) {
  113. /*
  114. * Handle the case where tpm2 is specified without tool-name or help
  115. */
  116. execlp("man", "man", "tpm2", NULL);
  117. } else if (strncmp(manpage, "tpm2_", strlen("tpm2_"))) {
  118. /*
  119. * Handle the case where the tool is specified as tpm2< >tool-name
  120. */
  121. char man_tool_name[MAX_TOOL_NAME_LEN] = {'t','p','m','2','_'};
  122. strncat(man_tool_name, manpage,
  123. strlen(manpage) < (MAX_TOOL_NAME_LEN - strlen("tpm2_")) ?
  124. strlen(manpage) : (MAX_TOOL_NAME_LEN - strlen("tpm2_")));
  125. execlp("man", "man", man_tool_name, NULL);
  126. } else {
  127. /*
  128. * Handle the case where the tool is specified as tpm2<_>tool-name
  129. */
  130. execlp("man", "man", manpage, NULL);
  131. }
  132. } else {
  133. if (waitpid(pid, &status, 0) == -1) {
  134. LOG_ERR("Waiting for child process that executes man failed, error:"
  135. " %s", strerror(errno));
  136. return false;
  137. }
  138. return WEXITSTATUS(status) == 0;
  139. }
  140. return true;
  141. }
  142. static void show_version(const char *name) {
  143. const char *tcti_default = NULL;
  144. TSS2_TCTI_INFO *info = NULL;
  145. TSS2_RC rc = Tss2_TctiLdr_GetInfo(NULL, &info);
  146. if (rc == TSS2_RC_SUCCESS && info != NULL) {
  147. tcti_default = info->name;
  148. }
  149. printf("tool=\"%s\" version=\"%s\" tctis=\"libtss2-tctildr\" tcti-default=%s\n",
  150. name, VERSION, tcti_default);
  151. Tss2_TctiLdr_FreeInfo(&info);
  152. }
  153. void tpm2_print_usage(const char *command, struct tpm2_options *tool_opts) {
  154. unsigned int i;
  155. bool indent = true;
  156. char *command_copy;
  157. if (!tool_opts || !command) {
  158. return;
  159. }
  160. command_copy = strdup(command);
  161. printf("Usage: %s%s%s\n", basename(command_copy),
  162. tool_opts->callbacks.on_opt ? " [<options>]" : "",
  163. tool_opts->callbacks.on_arg ? " <arguments>" : "");
  164. free(command_copy);
  165. if (tool_opts->callbacks.on_opt) {
  166. printf("Where <options> are:\n");
  167. for (i = 0; i < tool_opts->len; i++) {
  168. struct option *opt = &tool_opts->long_opts[i];
  169. if (indent) {
  170. printf(" ");
  171. indent = false;
  172. } else {
  173. printf(" ");
  174. }
  175. if (isalpha(opt->val)) {
  176. printf("[ -%c | --%s%s]", opt->val, opt->name,
  177. opt->has_arg ? "=<value>" : "");
  178. }
  179. else {
  180. printf("[ --%s%s]", opt->name,
  181. opt->has_arg ? "=<value>" : "");
  182. }
  183. if ((i + 1) % 4 == 0) {
  184. printf("\n");
  185. indent = true;
  186. }
  187. }
  188. if (i % 4 != 0) {
  189. printf("\n");
  190. }
  191. }
  192. }
  193. tpm2_option_code tpm2_handle_options(int argc, char **argv,
  194. tpm2_options *tool_opts, tpm2_option_flags *flags,
  195. TSS2_TCTI_CONTEXT **tcti) {
  196. tpm2_option_code rc = tpm2_option_code_err;
  197. TSS2_RC rc_tcti;
  198. bool result = false;
  199. bool show_help = false;
  200. bool manpager = true;
  201. bool explicit_manpager = false;
  202. /*
  203. * Handy way to *try* and find all used options:
  204. * grep -rn case\ \'[a-zA-Z]\' | awk '{print $3}' | sed s/\'//g | sed s/\://g | sort | uniq | less
  205. */
  206. struct option long_options [] = {
  207. { "tcti", required_argument, NULL, 'T' },
  208. { "help", optional_argument, NULL, 'h' },
  209. { "verbose", no_argument, NULL, 'V' },
  210. { "quiet", no_argument, NULL, 'Q' },
  211. { "version", no_argument, NULL, 'v' },
  212. { "enable-errata", no_argument, NULL, 'Z' },
  213. };
  214. const char *tcti_conf_option = NULL;
  215. /* handle any options */
  216. const char* common_short_opts = "T:h::vVQZ";
  217. tpm2_options *opts = tpm2_options_new(common_short_opts,
  218. ARRAY_LEN(long_options), long_options, NULL, NULL, 0);
  219. if (!opts) {
  220. return tpm2_option_code_err;
  221. }
  222. /* Get the options from the tool */
  223. if (tool_opts) {
  224. result = tpm2_options_cat(&opts, tool_opts);
  225. if (!result) {
  226. goto out;
  227. }
  228. }
  229. optind = 1;
  230. /* Parse the options, calling the tool callback if unknown */
  231. int c;
  232. while ((c = getopt_long(argc, argv, opts->short_opts, opts->long_opts, NULL))
  233. != -1) {
  234. switch (c) {
  235. case 'T':
  236. if (opts->flags & TPM2_OPTIONS_NO_SAPI) {
  237. LOG_ERR("%s: tool doesn't support the TCTI option", argv[0]);
  238. goto out;
  239. }
  240. /* only attempt to get options from tcti option string */
  241. tcti_conf_option = optarg;
  242. break;
  243. case 'h':
  244. show_help = true;
  245. /*
  246. * argv[0] = "tool name"
  247. * argv[1] = "--help=no/man" argv[2] = 0
  248. */
  249. if (argv[optind - 1]) {
  250. if (!strcmp(argv[optind - 1], "--help=no-man") ||
  251. !strcmp(argv[optind - 1], "-h=no-man") ||
  252. (argc < optind && !strcmp(argv[optind], "no-man"))) {
  253. manpager = false;
  254. optind++;
  255. /*
  256. * argv[0] = "tool name"
  257. * argv[1] = "--help" argv[2] = "no/man"
  258. */
  259. } else if (!strcmp(argv[optind - 1], "--help=man") ||
  260. !strcmp(argv[optind - 1], "-h=man") ||
  261. (argc < optind && !strcmp(argv[optind], "man"))) {
  262. manpager = true;
  263. explicit_manpager = true;
  264. optind++;
  265. } else {
  266. /*
  267. * argv[0] = "tool name"
  268. * argv[1] = "--help" argv[2] = 0
  269. */
  270. if (optind >= argc && argc == 2) {
  271. manpager = false;
  272. } else {
  273. /*
  274. * ERROR
  275. */
  276. show_help = false;
  277. LOG_ERR("Unknown help argument, got: \"%s\"", argv[optind]);
  278. }
  279. }
  280. }
  281. goto out;
  282. break;
  283. case 'V':
  284. flags->verbose = 1;
  285. break;
  286. case 'Q':
  287. flags->quiet = 1;
  288. break;
  289. case 'v':
  290. show_version(argv[0]);
  291. rc = tpm2_option_code_stop;
  292. goto out;
  293. break;
  294. case 'Z':
  295. flags->enable_errata = 1;
  296. break;
  297. case '?':
  298. goto out;
  299. default:
  300. /* NULL on_opt handler and unknown option specified is an error */
  301. if (!tool_opts || !tool_opts->callbacks.on_opt) {
  302. LOG_ERR("Unknown option found: %c", c);
  303. goto out;
  304. }
  305. result = tool_opts->callbacks.on_opt(c, optarg);
  306. if (!result) {
  307. goto out;
  308. }
  309. }
  310. }
  311. char **tool_args = &argv[optind];
  312. int tool_argc = argc - optind;
  313. /* have args and no handler, error condition */
  314. if (tool_argc && (!tool_opts || !tool_opts->callbacks.on_arg)) {
  315. LOG_ERR("Got arguments but the tool takes no arguments");
  316. show_help = true;
  317. goto out;
  318. }
  319. /* have args and a handler to process */
  320. else if (tool_argc && tool_opts->callbacks.on_arg) {
  321. result = tool_opts->callbacks.on_arg(tool_argc, tool_args);
  322. if (!result) {
  323. goto out;
  324. }
  325. }
  326. /* Only init a TCTI if the tool needs it and if the -h/--help option isn't present */
  327. if (!show_help) {
  328. /* tool doesn't request a sapi, don't initialize one */
  329. if (!tool_opts || !(tool_opts->flags & TPM2_OPTIONS_NO_SAPI)) {
  330. if (tcti_conf_option == NULL)
  331. tcti_conf_option = tpm2_util_getenv(TPM2TOOLS_ENV_TCTI);
  332. else if (!strcmp(tcti_conf_option, "none")) {
  333. if (!tool_opts
  334. || !(tool_opts->flags & TPM2_OPTIONS_OPTIONAL_SAPI)) {
  335. LOG_ERR("Requested no tcti, but tool requires TCTI.");
  336. goto out;
  337. }
  338. goto none;
  339. }
  340. rc_tcti = Tss2_TctiLdr_Initialize(tcti_conf_option, tcti);
  341. if (rc_tcti != TSS2_RC_SUCCESS || !*tcti) {
  342. LOG_ERR("Could not load tcti, got: \"%s\"", tcti_conf_option);
  343. goto out;
  344. }
  345. /*
  346. * no loader requested ie --tcti=none is an error if tool
  347. * doesn't indicate an optional SAPI
  348. */
  349. if (!flags->enable_errata) {
  350. flags->enable_errata = !!tpm2_util_getenv(
  351. TPM2TOOLS_ENV_ENABLE_ERRATA);
  352. }
  353. }
  354. }
  355. none:
  356. rc = tpm2_option_code_continue;
  357. out:
  358. /*
  359. * If help output is selected via -h or indicated by an error that help output
  360. * is desirable, show it.
  361. *
  362. * However, 3 conditions are possible:
  363. * 1. Try manpager and success -- done, no need to show short help output.
  364. * 2. Try manpager and failure -- show short help output.
  365. * 3. Do not use manpager -- show short help output.
  366. *
  367. */
  368. if (show_help) {
  369. bool did_manpager = false;
  370. if (manpager) {
  371. did_manpager = execute_man(argv[0], explicit_manpager);
  372. }
  373. if (!did_manpager) {
  374. tpm2_print_usage(argv[0], tool_opts);
  375. }
  376. if (tcti_conf_option && strcmp(tcti_conf_option, "none")) {
  377. TSS2_TCTI_INFO *info = NULL;
  378. rc_tcti = Tss2_TctiLdr_GetInfo(tcti_conf_option, &info);
  379. if (rc_tcti == TSS2_RC_SUCCESS && info) {
  380. printf("\ntcti-help(%s): %s\n", info->name, info->config_help);
  381. }
  382. Tss2_TctiLdr_FreeInfo(&info);
  383. }
  384. rc = tpm2_option_code_stop;
  385. }
  386. tpm2_options_free(opts);
  387. return rc;
  388. }