123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447 |
- /* SPDX-License-Identifier: BSD-3-Clause */
- #include <errno.h>
- #include <stdbool.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <ctype.h>
- #include <fcntl.h>
- #include <libgen.h>
- #include <unistd.h>
- #include <tss2/tss2_tctildr.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <sys/stat.h>
- #include "config.h"
- #include "log.h"
- #include "tpm2_options.h"
- #ifndef VERSION
- #warning "VERSION Not known at compile time, not embedding..."
- #define VERSION "UNKNOWN"
- #endif
- #define TPM2TOOLS_ENV_TCTI "TPM2TOOLS_TCTI"
- #define TPM2TOOLS_ENV_ENABLE_ERRATA "TPM2TOOLS_ENABLE_ERRATA"
- tpm2_options *tpm2_options_new(const char *short_opts, size_t len,
- const struct option *long_opts, tpm2_option_handler on_opt,
- tpm2_arg_handler on_arg, uint32_t flags) {
- tpm2_options *opts = calloc(1, sizeof(*opts) + (sizeof(*long_opts) * len));
- if (!opts) {
- LOG_ERR("oom");
- return NULL;
- }
- /*
- * On NULL, just make it a zero length string so we don't have to keep
- * checking it for NULL.
- */
- if (!short_opts) {
- short_opts = "";
- }
- opts->short_opts = strdup(short_opts);
- if (!opts->short_opts) {
- LOG_ERR("oom");
- free(opts);
- return NULL;
- }
- opts->callbacks.on_opt = on_opt;
- opts->callbacks.on_arg = on_arg;
- opts->len = len;
- opts->flags = flags;
- memcpy(opts->long_opts, long_opts, len * sizeof(*long_opts));
- return opts;
- }
- bool tpm2_options_cat(tpm2_options **dest, tpm2_options *src) {
- tpm2_options *d = *dest;
- /* move the nested char * pointer first */
- size_t opts_len = strlen(d->short_opts) + strlen(src->short_opts) + 1;
- char *tmp_short = realloc(d->short_opts, opts_len);
- if (!tmp_short) {
- LOG_ERR("oom");
- return false;
- }
- strcat(tmp_short, src->short_opts);
- d->short_opts = tmp_short;
- /* now move the enclosing structure */
- size_t long_opts_len = d->len + src->len;
- /* +1 for a terminating NULL at the end of options array for getopt_long */
- tpm2_options *tmp = realloc(d,
- sizeof(*d) + ((long_opts_len + 1) * sizeof(d->long_opts[0])));
- if (!tmp) {
- LOG_ERR("oom");
- return false;
- }
- *dest = d = tmp;
- d->callbacks.on_arg = src->callbacks.on_arg;
- d->callbacks.on_opt = src->callbacks.on_opt;
- d->flags = src->flags;
- memcpy(&d->long_opts[d->len], src->long_opts,
- src->len * sizeof(src->long_opts[0]));
- /* length must be updated post memcpy as we need d->len to be the original offset */
- d->len = long_opts_len;
- /* NULL term for getopt_long */
- memset(&d->long_opts[d->len], 0, sizeof(d->long_opts[0]));
- return true;
- }
- void tpm2_options_free(tpm2_options *opts) {
- if (!opts) {
- return;
- }
- free(opts->short_opts);
- free(opts);
- }
- static bool execute_man(char *prog_name, bool show_errors) {
- pid_t pid;
- int status;
- if ((pid = fork()) < 0) {
- LOG_ERR("Could not fork process to execute man, error: %s",
- strerror(errno));
- return false;
- }
- #define MAX_TOOL_NAME_LEN 64
- if (pid == 0) {
- if (!show_errors) {
- /* redirect manpager errors to stderr */
- int fd = open("/dev/null", O_WRONLY);
- if (fd < 0) {
- LOG_ERR("Could not open /dev/null");
- return false;
- }
- dup2(fd, 2);
- close(fd);
- }
- const char *manpage = basename(prog_name);
- if (!strcmp(manpage, "tpm2")) {
- /*
- * Handle the case where tpm2 is specified without tool-name or help
- */
- execlp("man", "man", "tpm2", NULL);
- } else if (strncmp(manpage, "tpm2_", strlen("tpm2_"))) {
- /*
- * Handle the case where the tool is specified as tpm2< >tool-name
- */
- char man_tool_name[MAX_TOOL_NAME_LEN] = {'t','p','m','2','_'};
- strncat(man_tool_name, manpage,
- strlen(manpage) < (MAX_TOOL_NAME_LEN - strlen("tpm2_")) ?
- strlen(manpage) : (MAX_TOOL_NAME_LEN - strlen("tpm2_")));
- execlp("man", "man", man_tool_name, NULL);
- } else {
- /*
- * Handle the case where the tool is specified as tpm2<_>tool-name
- */
- execlp("man", "man", manpage, NULL);
- }
- } else {
- if (waitpid(pid, &status, 0) == -1) {
- LOG_ERR("Waiting for child process that executes man failed, error:"
- " %s", strerror(errno));
- return false;
- }
- return WEXITSTATUS(status) == 0;
- }
- return true;
- }
- static void show_version(const char *name) {
- const char *tcti_default = NULL;
- TSS2_TCTI_INFO *info = NULL;
- TSS2_RC rc = Tss2_TctiLdr_GetInfo(NULL, &info);
- if (rc == TSS2_RC_SUCCESS && info != NULL) {
- tcti_default = info->name;
- }
- printf("tool=\"%s\" version=\"%s\" tctis=\"libtss2-tctildr\" tcti-default=%s\n",
- name, VERSION, tcti_default);
- Tss2_TctiLdr_FreeInfo(&info);
- }
- void tpm2_print_usage(const char *command, struct tpm2_options *tool_opts) {
- unsigned int i;
- bool indent = true;
- char *command_copy;
- if (!tool_opts || !command) {
- return;
- }
- command_copy = strdup(command);
- printf("Usage: %s%s%s\n", basename(command_copy),
- tool_opts->callbacks.on_opt ? " [<options>]" : "",
- tool_opts->callbacks.on_arg ? " <arguments>" : "");
- free(command_copy);
- if (tool_opts->callbacks.on_opt) {
- printf("Where <options> are:\n");
- for (i = 0; i < tool_opts->len; i++) {
- struct option *opt = &tool_opts->long_opts[i];
- if (indent) {
- printf(" ");
- indent = false;
- } else {
- printf(" ");
- }
- if (isalpha(opt->val)) {
- printf("[ -%c | --%s%s]", opt->val, opt->name,
- opt->has_arg ? "=<value>" : "");
- }
- else {
- printf("[ --%s%s]", opt->name,
- opt->has_arg ? "=<value>" : "");
- }
- if ((i + 1) % 4 == 0) {
- printf("\n");
- indent = true;
- }
- }
- if (i % 4 != 0) {
- printf("\n");
- }
- }
- }
- tpm2_option_code tpm2_handle_options(int argc, char **argv,
- tpm2_options *tool_opts, tpm2_option_flags *flags,
- TSS2_TCTI_CONTEXT **tcti) {
- tpm2_option_code rc = tpm2_option_code_err;
- TSS2_RC rc_tcti;
- bool result = false;
- bool show_help = false;
- bool manpager = true;
- bool explicit_manpager = false;
- /*
- * Handy way to *try* and find all used options:
- * grep -rn case\ \'[a-zA-Z]\' | awk '{print $3}' | sed s/\'//g | sed s/\://g | sort | uniq | less
- */
- struct option long_options [] = {
- { "tcti", required_argument, NULL, 'T' },
- { "help", optional_argument, NULL, 'h' },
- { "verbose", no_argument, NULL, 'V' },
- { "quiet", no_argument, NULL, 'Q' },
- { "version", no_argument, NULL, 'v' },
- { "enable-errata", no_argument, NULL, 'Z' },
- };
- const char *tcti_conf_option = NULL;
- /* handle any options */
- const char* common_short_opts = "T:h::vVQZ";
- tpm2_options *opts = tpm2_options_new(common_short_opts,
- ARRAY_LEN(long_options), long_options, NULL, NULL, 0);
- if (!opts) {
- return tpm2_option_code_err;
- }
- /* Get the options from the tool */
- if (tool_opts) {
- result = tpm2_options_cat(&opts, tool_opts);
- if (!result) {
- goto out;
- }
- }
- optind = 1;
- /* Parse the options, calling the tool callback if unknown */
- int c;
- while ((c = getopt_long(argc, argv, opts->short_opts, opts->long_opts, NULL))
- != -1) {
- switch (c) {
- case 'T':
- if (opts->flags & TPM2_OPTIONS_NO_SAPI) {
- LOG_ERR("%s: tool doesn't support the TCTI option", argv[0]);
- goto out;
- }
- /* only attempt to get options from tcti option string */
- tcti_conf_option = optarg;
- break;
- case 'h':
- show_help = true;
- /*
- * argv[0] = "tool name"
- * argv[1] = "--help=no/man" argv[2] = 0
- */
- if (argv[optind - 1]) {
- if (!strcmp(argv[optind - 1], "--help=no-man") ||
- !strcmp(argv[optind - 1], "-h=no-man") ||
- (argc < optind && !strcmp(argv[optind], "no-man"))) {
- manpager = false;
- optind++;
- /*
- * argv[0] = "tool name"
- * argv[1] = "--help" argv[2] = "no/man"
- */
- } else if (!strcmp(argv[optind - 1], "--help=man") ||
- !strcmp(argv[optind - 1], "-h=man") ||
- (argc < optind && !strcmp(argv[optind], "man"))) {
- manpager = true;
- explicit_manpager = true;
- optind++;
- } else {
- /*
- * argv[0] = "tool name"
- * argv[1] = "--help" argv[2] = 0
- */
- if (optind >= argc && argc == 2) {
- manpager = false;
- } else {
- /*
- * ERROR
- */
- show_help = false;
- LOG_ERR("Unknown help argument, got: \"%s\"", argv[optind]);
- }
- }
- }
- goto out;
- break;
- case 'V':
- flags->verbose = 1;
- break;
- case 'Q':
- flags->quiet = 1;
- break;
- case 'v':
- show_version(argv[0]);
- rc = tpm2_option_code_stop;
- goto out;
- break;
- case 'Z':
- flags->enable_errata = 1;
- break;
- case '?':
- goto out;
- default:
- /* NULL on_opt handler and unknown option specified is an error */
- if (!tool_opts || !tool_opts->callbacks.on_opt) {
- LOG_ERR("Unknown option found: %c", c);
- goto out;
- }
- result = tool_opts->callbacks.on_opt(c, optarg);
- if (!result) {
- goto out;
- }
- }
- }
- char **tool_args = &argv[optind];
- int tool_argc = argc - optind;
- /* have args and no handler, error condition */
- if (tool_argc && (!tool_opts || !tool_opts->callbacks.on_arg)) {
- LOG_ERR("Got arguments but the tool takes no arguments");
- show_help = true;
- goto out;
- }
- /* have args and a handler to process */
- else if (tool_argc && tool_opts->callbacks.on_arg) {
- result = tool_opts->callbacks.on_arg(tool_argc, tool_args);
- if (!result) {
- goto out;
- }
- }
- /* Only init a TCTI if the tool needs it and if the -h/--help option isn't present */
- if (!show_help) {
- /* tool doesn't request a sapi, don't initialize one */
- if (!tool_opts || !(tool_opts->flags & TPM2_OPTIONS_NO_SAPI)) {
- if (tcti_conf_option == NULL)
- tcti_conf_option = tpm2_util_getenv(TPM2TOOLS_ENV_TCTI);
- else if (!strcmp(tcti_conf_option, "none")) {
- if (!tool_opts
- || !(tool_opts->flags & TPM2_OPTIONS_OPTIONAL_SAPI)) {
- LOG_ERR("Requested no tcti, but tool requires TCTI.");
- goto out;
- }
- goto none;
- }
- rc_tcti = Tss2_TctiLdr_Initialize(tcti_conf_option, tcti);
- if (rc_tcti != TSS2_RC_SUCCESS || !*tcti) {
- LOG_ERR("Could not load tcti, got: \"%s\"", tcti_conf_option);
- goto out;
- }
- /*
- * no loader requested ie --tcti=none is an error if tool
- * doesn't indicate an optional SAPI
- */
- if (!flags->enable_errata) {
- flags->enable_errata = !!tpm2_util_getenv(
- TPM2TOOLS_ENV_ENABLE_ERRATA);
- }
- }
- }
- none:
- rc = tpm2_option_code_continue;
- out:
- /*
- * If help output is selected via -h or indicated by an error that help output
- * is desirable, show it.
- *
- * However, 3 conditions are possible:
- * 1. Try manpager and success -- done, no need to show short help output.
- * 2. Try manpager and failure -- show short help output.
- * 3. Do not use manpager -- show short help output.
- *
- */
- if (show_help) {
- bool did_manpager = false;
- if (manpager) {
- did_manpager = execute_man(argv[0], explicit_manpager);
- }
- if (!did_manpager) {
- tpm2_print_usage(argv[0], tool_opts);
- }
- if (tcti_conf_option && strcmp(tcti_conf_option, "none")) {
- TSS2_TCTI_INFO *info = NULL;
- rc_tcti = Tss2_TctiLdr_GetInfo(tcti_conf_option, &info);
- if (rc_tcti == TSS2_RC_SUCCESS && info) {
- printf("\ntcti-help(%s): %s\n", info->name, info->config_help);
- }
- Tss2_TctiLdr_FreeInfo(&info);
- }
- rc = tpm2_option_code_stop;
- }
- tpm2_options_free(opts);
- return rc;
- }
|