| /* |
| * tpm_bios -- |
| * |
| * Authors: Ken Goldman <kgoldman@us.ibm.com> |
| * Stefan Berger <stefanb@us.ibm.com> |
| * |
| * (c) Copyright IBM Corporation 2014. |
| * |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * Neither the names of the IBM Corporation nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| #include <stdio.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <sys/socket.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <netdb.h> |
| #include <sys/un.h> |
| #include <getopt.h> |
| #include <signal.h> |
| |
| #include "sys_dependencies.h" |
| #include "swtpm.h" |
| #include "tpm_bios.h" |
| |
| /* |
| * durations of the commands |
| * On slow machines with much concurrency short timeouts may result in |
| * errors; so we scale them up by 10. |
| */ |
| #define TPM_DURATION_SHORT (2 * 10) /* seconds */ |
| #define TPM_DURATION_MEDIUM (20 * 10) /* seconds */ |
| #define TPM_DURATION_LONG (60 * 10) /* seconds */ |
| |
| #define MIN(A, B) ((A) < (B) ? (A) : (B)) |
| |
| #define DEFAULT_TCP_PORT 6545 |
| |
| static char *tpm_device; /* e.g., /dev/tpm0 */ |
| |
| static char *tcp_hostname; |
| static unsigned int tcp_port = DEFAULT_TCP_PORT; |
| |
| static char *unix_path; |
| |
| static int parse_tcp_optarg(char *optarg, char **tcp_hostname, |
| unsigned int *tcp_port) |
| { |
| char *pos = strrchr(optarg, ':'); |
| int n; |
| |
| *tcp_port = DEFAULT_TCP_PORT; |
| |
| if (!pos) { |
| /* <server> */ |
| *tcp_hostname = strdup(optarg); |
| if (*tcp_hostname == NULL) { |
| fprintf(stderr, "Out of memory.\n"); |
| return -1; |
| } |
| return 0; |
| } else if (pos == optarg) { |
| if (strlen(&pos[1]) != 0) { |
| /* :<port> (not just ':') */ |
| n = sscanf(&pos[1], "%u", tcp_port); |
| if (n != 1) { |
| fprintf(stderr, "Invalid port '%s'\n", &pos[1]); |
| return -1; |
| } |
| if (*tcp_port >= 65536) { |
| fprintf(stderr, "Port '%s' outside valid range.\n", |
| &optarg[1]); |
| return -1; |
| } |
| } |
| |
| *tcp_hostname = strdup("127.0.0.1"); |
| if (*tcp_hostname == NULL) { |
| fprintf(stderr, "Out of memory.\n"); |
| return -1; |
| } |
| } else { |
| /* <server>:<port> */ |
| n = sscanf(&pos[1], "%u", tcp_port); |
| if (n != 1) { |
| fprintf(stderr, "Invalid port '%s'\n", &pos[1]); |
| return -1; |
| } |
| if (*tcp_port >= 65536) { |
| fprintf(stderr, "Port '%s' outside valid range.\n", |
| &optarg[1]); |
| return -1; |
| } |
| |
| *tcp_hostname = strndup(optarg, pos - optarg); |
| if (*tcp_hostname == NULL) { |
| fprintf(stderr, "Out of memory.\n"); |
| return -1; |
| } |
| } |
| return 0; |
| } |
| |
| static int open_connection(char *devname, char *tcp_device_hostname, |
| unsigned int tcp_device_port, const char *unix_path) |
| { |
| int fd = -1; |
| char *tcp_device_port_string = NULL; |
| |
| if (devname) |
| goto use_device; |
| |
| if (tcp_device_hostname) |
| goto use_tcp; |
| |
| if (unix_path) { |
| struct sockaddr_un addr; |
| size_t unix_path_len = strlen(unix_path) + 1; |
| |
| if (unix_path_len > sizeof(addr.sun_path)) { |
| fprintf(stderr, "Socket path is too long.\n"); |
| return -1; |
| } |
| |
| fd = socket(AF_UNIX, SOCK_STREAM, 0); |
| if (fd > 0) { |
| addr.sun_family = AF_UNIX; |
| strncpy(addr.sun_path, unix_path, unix_path_len); |
| |
| if (connect(fd, |
| (struct sockaddr*)&addr, sizeof(addr)) < 0) { |
| close(fd); |
| fd = -1; |
| } |
| } |
| |
| if (fd < 0) { |
| fprintf(stderr, "Could not connect using UnixIO socket.\n"); |
| } |
| return fd; |
| } |
| |
| if (getenv("TCSD_USE_TCP_DEVICE")) { |
| struct addrinfo hints; |
| struct addrinfo *ais = NULL, *ai; |
| char portstr[10]; |
| int err; |
| |
| if ((tcp_device_hostname = getenv("TCSD_TCP_DEVICE_HOSTNAME")) == NULL) |
| tcp_device_hostname = "localhost"; |
| if ((tcp_device_port_string = getenv("TCSD_TCP_DEVICE_PORT")) != NULL) |
| tcp_device_port = atoi(tcp_device_port_string); |
| else |
| tcp_device_port = DEFAULT_TCP_PORT; |
| |
| use_tcp: |
| memset(&hints, 0, sizeof(hints)); |
| hints.ai_family = AF_UNSPEC; |
| hints.ai_socktype = SOCK_STREAM; |
| |
| snprintf(portstr, sizeof(portstr), "%u", tcp_device_port); |
| |
| err = getaddrinfo(tcp_device_hostname, portstr, &hints, &ais); |
| if (err != 0) { |
| fprintf(stderr, "getaddrinfo failed on host '%s': %s\n", |
| tcp_device_hostname, gai_strerror(err)); |
| return -1; |
| } |
| |
| for (ai = ais; ai != NULL; ai = ai->ai_next) { |
| fd = socket(ai->ai_family, ai->ai_socktype, 0); |
| if (fd < 0) |
| continue; |
| |
| if (connect(fd, (struct sockaddr *)ai->ai_addr, |
| ai->ai_addrlen) == 0) |
| break; |
| |
| close(fd); |
| fd = -1; |
| } |
| freeaddrinfo(ais); |
| |
| if (fd < 0) { |
| fprintf(stderr, "Could not connect using TCP socket.\n"); |
| } |
| } else { |
| use_device: |
| if (!devname) |
| devname = getenv("TPM_DEVICE"); |
| if (!devname) |
| devname = "/dev/tpm0"; |
| |
| fd = open(devname, O_RDWR); |
| if (fd < 0) { |
| fprintf(stderr, "Unable to open device '%s'.\n", devname ); |
| } |
| } |
| |
| return fd; |
| } |
| |
| |
| static int talk(const void *cmd, size_t count, int *tpm_errcode, |
| unsigned int to_seconds, |
| struct tpm_resp_header *res, size_t res_size) |
| { |
| ssize_t len; |
| size_t pkt_len; |
| int rc = -1; |
| int fd, n; |
| unsigned char buffer[1024]; |
| struct timeval timeout = { |
| .tv_sec = to_seconds, |
| .tv_usec = 0, |
| }; |
| fd_set rfds; |
| struct tpm_resp_header *hdr; |
| |
| fd = open_connection(tpm_device, tcp_hostname, tcp_port, unix_path); |
| if (fd < 0) { |
| goto err_exit; |
| } |
| |
| len = write(fd, cmd, count); |
| if (len < 0 || (size_t)len != count) { |
| fprintf(stderr, "Write to file descriptor failed.\n"); |
| goto err_close_fd; |
| } |
| |
| FD_ZERO(&rfds); |
| FD_SET(fd, &rfds); |
| |
| n = select(fd + 1, &rfds, NULL, NULL, &timeout); |
| if (n == 0) { |
| fprintf(stderr, "TPM did not respond after %u seconds.\n", |
| to_seconds); |
| goto err_close_fd; |
| } else if (n < 0) { |
| fprintf(stderr, "Error on select call: %s\n", strerror(errno)); |
| goto err_close_fd; |
| } |
| |
| len = read(fd, buffer, sizeof(buffer)); |
| if (len < 0) { |
| fprintf(stderr, "Read from file descriptor failed.\n"); |
| goto err_close_fd; |
| } |
| |
| if (len < 10) { |
| fprintf(stderr, "Returned packet is too short.\n"); |
| goto err_close_fd; |
| } |
| |
| hdr = (struct tpm_resp_header *)buffer; |
| pkt_len = be32toh(hdr->length); |
| if ((unsigned int)len != pkt_len) { |
| fprintf(stderr, "Malformed response.\n"); |
| goto err_close_fd; |
| } |
| |
| if (res) |
| memcpy(res, buffer, MIN(pkt_len, res_size)); |
| |
| *tpm_errcode = be32toh(hdr->result); |
| |
| rc = 0; |
| |
| err_close_fd: |
| close(fd); |
| |
| err_exit: |
| return rc; |
| } |
| |
| static int TPM_Startup(unsigned char parm, int *tpm_errcode) |
| { |
| struct tpm_startup tss = { |
| .hdr = { |
| .tag = htobe16(TPM_TAG_RQU_COMMAND), |
| .length = htobe32(sizeof(tss)), |
| .ordinal = htobe32(TPM_ORD_Startup), |
| }, |
| .startup_type = htobe16(parm), |
| }; |
| |
| return talk(&tss, sizeof(tss), tpm_errcode, TPM_DURATION_SHORT, |
| NULL, 0); |
| } |
| |
| static int TSC_PhysicalPresence(unsigned short physical_presence, |
| int *tpm_errcode) |
| { |
| struct tsc_physical_presence tpp = { |
| .hdr = { |
| .tag = htobe16(TPM_TAG_RQU_COMMAND), |
| .length = htobe32(sizeof(tpp)), |
| .ordinal = htobe32(TPM_ORD_PhysicalPresence), |
| }, |
| .physical_presence = htobe16(physical_presence), |
| }; |
| |
| return talk(&tpp, sizeof(tpp), tpm_errcode, TPM_DURATION_SHORT, |
| NULL, 0); |
| } |
| |
| static int TPM_GetCapability_Subcap(uint32_t cap, uint32_t subcap, |
| struct tpm_resp_header *res, size_t res_size, |
| int *tpm_errcode) |
| { |
| struct tpm_get_capability_subcap tgc = { |
| .hdr = { |
| .tag = htobe16(TPM_TAG_RQU_COMMAND), |
| .length = htobe32(sizeof(tgc)), |
| .ordinal = htobe32(TPM_ORD_GetCapability), |
| }, |
| .cap = htobe32(cap), |
| .subcap_size = htobe32(sizeof(tgc.subcap)), |
| .subcap = htobe32(subcap), |
| }; |
| |
| return talk(&tgc, sizeof(tgc), tpm_errcode, TPM_DURATION_SHORT, |
| res, res_size); |
| } |
| |
| static int TPM_PhysicalEnable(int *tpm_errcode) |
| { |
| struct tpm_physical_enable tpe = { |
| .hdr = { |
| .tag = htobe16(TPM_TAG_RQU_COMMAND), |
| .length = htobe32(sizeof(tpe)), |
| .ordinal = htobe32(TPM_ORD_PhysicalEnable), |
| }, |
| }; |
| |
| return talk(&tpe, sizeof(tpe), tpm_errcode, TPM_DURATION_SHORT, |
| NULL, 0); |
| } |
| |
| static int TPM_PhysicalSetDeactivated(unsigned char parm, int *tpm_errcode) |
| { |
| struct tpm_physical_set_deactivated tpsd = { |
| .hdr = { |
| .tag = htobe16(TPM_TAG_RQU_COMMAND), |
| .length = htobe32(sizeof(tpsd)), |
| .ordinal = htobe32(TPM_ORD_PhysicalSetDeactivated), |
| }, |
| .state = parm, |
| }; |
| |
| return talk(&tpsd, sizeof(tpsd), tpm_errcode, TPM_DURATION_SHORT, |
| NULL, 0); |
| } |
| |
| static int TPM_ContinueSelfTest(int *tpm_errcode) |
| { |
| struct tpm_continue_selftest tcs = { |
| .hdr = { |
| .tag = htobe16(TPM_TAG_RQU_COMMAND), |
| .length = htobe32(sizeof(tcs)), |
| .ordinal = htobe32(TPM_ORD_ContinueSelfTest), |
| }, |
| }; |
| |
| return talk(&tcs, sizeof(tcs), tpm_errcode, TPM_DURATION_LONG, |
| NULL, 0); |
| } |
| |
| static int TPM2_Startup(unsigned short startup_type, int *tpm_errcode) |
| { |
| struct tpm2_startup ts = { |
| .hdr = { |
| .tag = htobe16(TPM2_ST_NO_SESSIONS), |
| .length = htobe32(sizeof(ts)), |
| .ordinal = htobe32(TPM2_CC_Startup), |
| }, |
| .startup_type = htobe16(startup_type), |
| }; |
| |
| return talk(&ts, sizeof(ts), tpm_errcode, |
| TPM_DURATION_SHORT, NULL, 0); |
| } |
| |
| static int TPM2_IncrementalSelfTest(int *tpm_errcode) |
| { |
| struct tpm2_incremental_selftest ts = { |
| .hdr = { |
| .tag = htobe16(TPM2_ST_NO_SESSIONS), |
| .length = htobe32(sizeof(ts)), |
| .ordinal = htobe32(TPM2_CC_IncrementalSelfTest), |
| }, |
| .to_test = { |
| .num_entries = htobe32(1), |
| .algids = { |
| htobe16(TPM2_ALG_SHA256), |
| }, |
| }, |
| }; |
| |
| return talk(&ts, sizeof(ts), tpm_errcode, |
| TPM_DURATION_SHORT, NULL, 0); |
| } |
| |
| static int TPM2_HierarchyChangeAuth(int *tpm_errcode) |
| { |
| struct tpm2_hierarchy_change_auth thca = { |
| .hdr = { |
| .tag = htobe16(TPM2_ST_SESSIONS), |
| .length = htobe32(sizeof(thca)), |
| .ordinal = htobe32(TPM2_CC_HierarchyChangeAuth), |
| }, |
| .authhandle = htobe32(TPM2_RH_PLATFORM), |
| .authblock_size = htobe32(sizeof(thca.authblock)), |
| .authblock = { |
| .handle = htobe32(TPM2_RS_PW), |
| .nonce_size = htobe16(0), |
| .cont = 1, |
| .password_size = htobe16(0), |
| }, |
| .newauth = { |
| .size = htobe16(sizeof(thca.newauth.buffer)), |
| }, |
| }; |
| |
| int fd = open("/dev/urandom", O_RDONLY); |
| if (fd >= 0) { |
| ssize_t n = read(fd, &thca.newauth.buffer, |
| sizeof(thca.newauth.buffer)); |
| close(fd); |
| if (n != sizeof(thca.newauth.buffer)) { |
| printf("Read of bytes from /dev/urandom failed"); |
| if (n < 0) |
| printf(": %s", strerror(errno)); |
| printf("\n"); |
| return -1; |
| } |
| } else { |
| printf("Could not open /dev/urandom: %s\n", strerror(errno)); |
| return -1; |
| } |
| |
| return talk(&thca, sizeof(thca), tpm_errcode, |
| TPM_DURATION_SHORT, NULL, 0); |
| } |
| |
| static int tpm12_bios(int do_more, int contselftest, unsigned char startupparm, |
| int unassert_pp, int ensure_activated) |
| { |
| int ret = 0; |
| int tpm_errcode = 0; |
| int tpm_error = 0; |
| unsigned short physical_presence; |
| struct tpm_get_capability_permflags_res perm_flags; |
| |
| if (ret == 0) { |
| if (0xff != startupparm) { |
| ret = TPM_Startup(startupparm, &tpm_errcode); |
| if (tpm_errcode != 0) { |
| tpm_error = 1; |
| fprintf(stderr, "TPM_Startup(0x%02x) returned " |
| "error code 0x%08x\n", |
| startupparm, tpm_errcode); |
| } |
| } |
| } |
| |
| /* Sends the TSC_PhysicalPresence command to turn on physicalPresenceCMDEnable */ |
| if ((ret == 0) && do_more) { |
| physical_presence = TPM_PHYSICAL_PRESENCE_CMD_ENABLE; |
| ret = TSC_PhysicalPresence(physical_presence, &tpm_errcode); |
| if (tpm_errcode != 0) { |
| tpm_error = 1; |
| fprintf(stderr, "TSC_PhysicalPresence(CMD_ENABLE) " |
| "returned error code 0x%08x\n", tpm_errcode); |
| } |
| } |
| |
| /* Sends the TSC_PhysicalPresence command to turn on physicalPresence */ |
| if ((ret == 0) && do_more) { |
| physical_presence = TPM_PHYSICAL_PRESENCE_PRESENT; |
| ret = TSC_PhysicalPresence(physical_presence, &tpm_errcode); |
| if (tpm_errcode != 0) { |
| tpm_error = 1; |
| fprintf(stderr, "TSC_PhysicalPresence(PRESENT) " |
| "returned error code 0x%08x\n", tpm_errcode); |
| } |
| } |
| /* Determine the permanent flags */ |
| if ((ret == 0) && do_more && ensure_activated) { |
| ret = TPM_GetCapability_Subcap(TPM_CAP_FLAG, TPM_CAP_FLAG_PERMANENT, |
| &perm_flags.hdr, sizeof(perm_flags), |
| &tpm_errcode); |
| if (tpm_errcode != 0) { |
| tpm_error = 1; |
| fprintf(stderr, "TPM_GetCapability() returned error " |
| "code 0x%08x\n", tpm_errcode); |
| } |
| } |
| /* Sends the TPM_Process_PhysicalEnable command to clear disabled */ |
| if ((ret == 0) && do_more) { |
| ret = TPM_PhysicalEnable(&tpm_errcode); |
| if (tpm_errcode != 0) { |
| tpm_error = 1; |
| fprintf(stderr, "TPM_PhysicalEnable returned error " |
| "code 0x%08x\n", tpm_errcode); |
| } |
| } |
| /* Sends the TPM_Process_PhysicalSetDeactivated command to clear deactivated */ |
| if ((ret == 0) && do_more) { |
| ret = TPM_PhysicalSetDeactivated(0, &tpm_errcode); |
| if (tpm_errcode != 0) { |
| tpm_error = 1; |
| fprintf(stderr, "TPM_PhysicalSetDeactivated returned " |
| "error code 0x%08x\n", tpm_errcode); |
| } |
| if (ensure_activated) { |
| /* activation will require resetting the TPM */ |
| if (perm_flags.flags[TPM_PERM_FLAG_DEACTIVATED_IDX]) { |
| ret = 0x81; |
| printf("TPM requires a reset\n"); |
| } |
| } |
| } |
| |
| if ((ret == 0) && contselftest) { |
| ret = TPM_ContinueSelfTest(&tpm_errcode); |
| if (tpm_errcode != 0) { |
| tpm_error = 1; |
| fprintf(stderr, "TPM_ContinueSelfTest returned error " |
| "code 0x%08x\n", tpm_errcode); |
| } |
| } |
| |
| /* Sends the TSC_PhysicalPresence command to turn on physicalPresenceCMDEnable */ |
| if ((ret == 0) && unassert_pp) { |
| physical_presence = TPM_PHYSICAL_PRESENCE_CMD_ENABLE; |
| ret = TSC_PhysicalPresence(physical_presence, &tpm_errcode); |
| if (tpm_errcode != 0) { |
| tpm_error = 1; |
| fprintf(stderr, |
| "TSC_PhysicalPresence(CMD_ENABLE) returned " |
| "error code 0x%08x\n", tpm_errcode); |
| } |
| } |
| |
| /* Sends the TSC_PhysicalPresence command to unassert physical presence and lock it */ |
| if ((ret == 0) && unassert_pp) { |
| physical_presence = TPM_PHYSICAL_PRESENCE_NOTPRESENT | |
| TPM_PHYSICAL_PRESENCE_LOCK; |
| ret = TSC_PhysicalPresence(physical_presence, &tpm_errcode); |
| if (tpm_errcode != 0) { |
| tpm_error = 1; |
| fprintf(stderr, "TSC_PhysicalPresence(NOT_PRESENT|LOCK) " |
| "returned error code 0x%08x\n", tpm_errcode); |
| } |
| } |
| |
| if (!ret && tpm_error) |
| ret = 0x80; |
| |
| return ret; |
| } |
| |
| static int tpm2_bios(int contselftest, unsigned char startupparm, |
| int set_password) |
| { |
| int ret = 0; |
| int tpm_errcode = 0; |
| int tpm_error = 0; |
| |
| if (ret == 0) { |
| if (0xff != startupparm) { |
| ret = TPM2_Startup(startupparm, &tpm_errcode); |
| if (tpm_errcode != 0) { |
| tpm_error = 1; |
| printf("TPM2_Startup returned error code " |
| "0x%08x\n", tpm_errcode); |
| } |
| } |
| } |
| |
| if ((ret == 0) && contselftest) { |
| ret = TPM2_IncrementalSelfTest(&tpm_errcode); |
| if (tpm_errcode != 0) { |
| tpm_error = 1; |
| printf("TPM2_IncrementalSelfTest returned error " |
| "code 0x%08x\n", tpm_errcode); |
| } |
| } |
| |
| if ((ret == 0) && set_password) { |
| ret = TPM2_HierarchyChangeAuth(&tpm_errcode); |
| if (tpm_errcode != 0) { |
| tpm_error = 1; |
| printf("TPM2_HierarchyChangeAuth returned error " |
| "code 0x%08x\n", tpm_errcode); |
| } |
| } |
| |
| if (!ret && tpm_error) |
| ret = 0x80; |
| |
| return ret; |
| } |
| |
| static void versioninfo(void) |
| { |
| printf( |
| "TPM emulator BIOS emulator version %d.%d.%d, Copyright (c) 2015 IBM Corp.\n" |
| ,SWTPM_VER_MAJOR, SWTPM_VER_MINOR, SWTPM_VER_MICRO); |
| } |
| |
| static void print_usage(const char *prgname) |
| { |
| versioninfo(); |
| printf( |
| "\n" |
| "%s [options]\n" |
| "\n" |
| "Runs TPM_Startup (unless -n), then (unless -o) sets PP, enable, activate \n" |
| "and finally (using -u) gives up physical presence (PP)\n" |
| "\n" |
| "The following options are supported:\n" |
| "\t--tpm-device <device> use the given device; default is /dev/tpm0\n" |
| "\t--tcp [<host>]:[<prt>] connect to TPM on given host and port;\n" |
| "\t default host is 127.0.0.1, default port is %u\n" |
| "\t--unix <path> connect to TPM using UnixIO socket\n" |
| "\t--tpm2 initialize a TPM2\n" |
| "\t-c startup clear (default)\n" |
| "\t-s startup state\n" |
| "\t-d startup deactivate (no effect on TPM2)\n" |
| "\t-n no startup\n" |
| "\t-o startup only\n" |
| "\t-cs run TPM_ContinueSelfTest on TPM1.2\n" |
| "\t run TPM2_IncrementalSelfTest on TPM2\n" |
| "\t-ea make sure that the TPM 1.2 is activated;\n" |
| "\t terminate with exit code 129 if the TPM\n" |
| "\t needs to be reset\n" |
| "\t-u give up physical presence\n" |
| "\t on TPM 2 set the platform hierarchy to a\n" |
| "\t random password\n" |
| "\t-v display version and exit\n" |
| "\t-h display this help screen and exit\n" |
| , prgname, DEFAULT_TCP_PORT); |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| int ret = 0; |
| int do_more = 1; |
| int ensure_activated = 0; |
| int contselftest = 0; |
| unsigned char startupparm = 0x1; /* parameter for TPM_Startup(); */ |
| unsigned char startupparm_tpm2 = 0x00; |
| int unassert_pp = 0; |
| int tpm2 = 0; |
| static struct option long_options[] = { |
| {"tpm-device", required_argument, NULL, 'D'}, |
| {"tcp", required_argument, NULL, 'T'}, |
| {"unix", required_argument, NULL, 'U'}, |
| {"c", no_argument, NULL, 'c'}, |
| {"d", no_argument, NULL, 'd'}, |
| {"h", no_argument, NULL, 'h'}, |
| {"v", no_argument, NULL, 'v'}, |
| {"n", no_argument, NULL, 'n'}, |
| {"s", no_argument, NULL, 's'}, |
| {"o", no_argument, NULL, 'o'}, |
| {"cs", no_argument, NULL, 'C'}, |
| {"ea", no_argument, NULL, 'E'}, |
| {"u", no_argument, NULL, 'u'}, |
| {"tpm2", no_argument, NULL, '2'}, |
| {NULL, 0, NULL, 0}, |
| }; |
| int opt, option_index = 0; |
| |
| #ifdef __NetBSD__ |
| while ((opt = getopt_long(argc, argv, "D:T:U:cdhvnsoCEu2", long_options, |
| &option_index)) != -1) { |
| #else |
| while ((opt = getopt_long_only(argc, argv, "", long_options, |
| &option_index)) != -1) { |
| #endif |
| switch (opt) { |
| case 'D': |
| free(tpm_device); |
| tpm_device = strdup(optarg); |
| if (!tpm_device) { |
| fprintf(stderr, "Out of memory."); |
| return EXIT_FAILURE; |
| } |
| break; |
| case 'T': |
| free(tcp_hostname); |
| if (parse_tcp_optarg(optarg, &tcp_hostname, &tcp_port) < 0) { |
| return EXIT_FAILURE; |
| } |
| break; |
| case 'U': |
| free(unix_path); |
| unix_path = strdup(optarg); |
| if (!unix_path) { |
| fprintf(stderr, "Out of memory.\n"); |
| return EXIT_FAILURE; |
| } |
| break; |
| case 'c': |
| startupparm = TPM_ST_CLEAR; |
| startupparm_tpm2 = TPM2_SU_CLEAR; |
| do_more = 1; |
| break; |
| case 'd': |
| startupparm = TPM_ST_DEACTIVATED; |
| startupparm_tpm2 = 0xff; |
| do_more = 0; |
| break; |
| case 'h': |
| print_usage(argv[0]); |
| return EXIT_SUCCESS; |
| case 'n': |
| startupparm = 0xff; |
| startupparm_tpm2 = 0xff; |
| do_more = 1; |
| break; |
| case 's': |
| startupparm = TPM_ST_STATE; |
| startupparm_tpm2 = TPM2_SU_STATE; |
| do_more = 1; |
| break; |
| case 'o': |
| do_more = 0; |
| break; |
| case 'C': |
| contselftest = 1; |
| break; |
| case 'E': |
| ensure_activated = 1; |
| break; |
| case 'u': |
| unassert_pp = 1; |
| break; |
| case '2': |
| tpm2 = 1; |
| break; |
| default: |
| print_usage(argv[0]); |
| return EXIT_FAILURE; |
| } |
| } |
| |
| if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { |
| fprintf(stderr, "Could not install signal handler for SIGPIPE."); |
| return EXIT_FAILURE; |
| } |
| |
| if (tpm2) { |
| ret = tpm2_bios(contselftest, startupparm_tpm2, |
| unassert_pp); |
| } else { |
| ret = tpm12_bios(do_more, contselftest, startupparm, |
| unassert_pp, ensure_activated); |
| } |
| |
| return ret; |
| } |