1/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 *
5 * TPM command utility.  Runs simple TPM commands.  Mostly useful when physical
6 * presence has not been locked.
7 *
8 * The exit code is 0 for success, the TPM error code for TPM errors, and 255
9 * for other errors.
10 */
11
12#include <stdint.h>
13#include <stdlib.h>
14#include <stdio.h>
15#include <string.h>
16#include <syslog.h>
17
18#include "tlcl.h"
19#include "tpm_error_messages.h"
20#include "tss_constants.h"
21
22#define OTHER_ERROR 255  /* OTHER_ERROR must be the largest uint8_t value. */
23
24typedef struct command_record {
25  const char* name;
26  const char* abbr;
27  const char* description;
28  uint32_t (*handler)(void);
29} command_record;
30
31/* Set in main, consumed by handler functions below.  We use global variables
32 * so we can also choose to call Tlcl*() functions directly; they don't take
33 * argv/argc.
34 */
35int nargs;
36char** args;
37
38/* Converts a string in the form 0x[0-9a-f]+ to a 32-bit value.  Returns 0 for
39 * success, non-zero for failure.
40 */
41int HexStringToUint32(const char* string, uint32_t* value) {
42  char tail[1];
43  /* strtoul is not as good because it overflows silently */
44  char* format = strncmp(string, "0x", 2) ? "%8x%s" : "0x%8x%s";
45  int n = sscanf(string, format, value, tail);
46  return n != 1;
47}
48
49/* Converts a string in the form [0-9a-f]+ to an 8-bit value.  Returns 0 for
50 * success, non-zero for failure.
51 */
52int HexStringToUint8(const char* string, uint8_t* value) {
53  char* end;
54  uint32_t large_value = strtoul(string, &end, 16);
55  if (*end != '\0' || large_value > 0xff) {
56    return 1;
57  }
58  *value = large_value;
59  return 0;
60}
61
62/* TPM error check and reporting.  Returns 0 if |result| is 0 (TPM_SUCCESS).
63 * Otherwise looks up a TPM error in the error table and prints the error if
64 * found.  Then returns min(result, OTHER_ERROR) since some error codes, such
65 * as TPM_E_RETRY, do not fit in a byte.
66 */
67uint8_t ErrorCheck(uint32_t result, const char* cmd) {
68  uint8_t exit_code = result > OTHER_ERROR ? OTHER_ERROR : result;
69  if (result == 0) {
70    return 0;
71  } else {
72    int i;
73    int n = sizeof(tpm_error_table) / sizeof(tpm_error_table[0]);
74    fprintf(stderr, "command \"%s\" failed with code 0x%x\n", cmd, result);
75    for (i = 0; i < n; i++) {
76      if (tpm_error_table[i].code == result) {
77        fprintf(stderr, "%s\n%s\n", tpm_error_table[i].name,
78                tpm_error_table[i].description);
79        return exit_code;
80      }
81    }
82    fprintf(stderr, "the TPM error code is unknown to this program\n");
83    return exit_code;
84  }
85}
86
87/* Handler functions.  These wouldn't exist if C had closures.
88 */
89static uint32_t HandlerGetFlags(void) {
90  uint8_t disabled;
91  uint8_t deactivated;
92  uint8_t nvlocked;
93  uint32_t result = TlclGetFlags(&disabled, &deactivated, &nvlocked);
94  if (result == 0) {
95    printf("disabled: %d\ndeactivated: %d\nnvlocked: %d\n",
96           disabled, deactivated, nvlocked);
97  }
98  return result;
99}
100
101static uint32_t HandlerActivate(void) {
102  return TlclSetDeactivated(0);
103}
104
105static uint32_t HandlerDeactivate(void) {
106  return TlclSetDeactivated(1);
107}
108
109static uint32_t HandlerDefineSpace(void) {
110  uint32_t index, size, perm;
111  if (nargs != 5) {
112    fprintf(stderr, "usage: tpmc def <index> <size> <perm>\n");
113    exit(OTHER_ERROR);
114  }
115  if (HexStringToUint32(args[2], &index) != 0 ||
116      HexStringToUint32(args[3], &size) != 0 ||
117      HexStringToUint32(args[4], &perm) != 0) {
118    fprintf(stderr, "<index>, <size>, and <perm> must be "
119            "32-bit hex (0x[0-9a-f]+)\n");
120    exit(OTHER_ERROR);
121  }
122  return TlclDefineSpace(index, perm, size);
123}
124
125static uint32_t HandlerWrite(void) {
126  uint32_t index, size;
127  uint8_t value[TPM_MAX_COMMAND_SIZE];
128  char** byteargs;
129  int i;
130  if (nargs < 3) {
131    fprintf(stderr, "usage: tpmc write <index> [<byte0> <byte1> ...]\n");
132    exit(OTHER_ERROR);
133  }
134  if (HexStringToUint32(args[2], &index) != 0) {
135    fprintf(stderr, "<index> must be 32-bit hex (0x[0-9a-f]+)\n");
136    exit(OTHER_ERROR);
137  }
138  size = nargs - 3;
139  if (size > sizeof(value)) {
140    fprintf(stderr, "byte array too large\n");
141    exit(OTHER_ERROR);
142  }
143
144  byteargs = args + 3;
145  for (i = 0; i < size; i++) {
146    if (HexStringToUint8(byteargs[i], &value[i]) != 0) {
147      fprintf(stderr, "invalid byte %s, should be [0-9a-f][0-9a-f]?\n",
148              byteargs[i]);
149      exit(OTHER_ERROR);
150    }
151  }
152
153  if (size == 0) {
154    if (index == TPM_NV_INDEX_LOCK) {
155      fprintf(stderr, "This would set the nvLocked bit. "
156              "Use \"tpmc setnv\" instead.\n");
157      exit(OTHER_ERROR);
158    }
159    printf("warning: zero-length write\n");
160  } else {
161    printf("writing %d byte%s\n", size, size > 1 ? "s" : "");
162  }
163
164  return TlclWrite(index, value, size);
165}
166
167static uint32_t HandlerPCRRead(void) {
168  uint32_t index;
169  uint8_t value[TPM_PCR_DIGEST];
170  uint32_t result;
171  int i;
172  if (nargs != 3) {
173    fprintf(stderr, "usage: tpmc pcrread <index>\n");
174    exit(OTHER_ERROR);
175  }
176  if (HexStringToUint32(args[2], &index) != 0) {
177    fprintf(stderr, "<index> must be 32-bit hex (0x[0-9a-f]+)\n");
178    exit(OTHER_ERROR);
179  }
180  result = TlclPCRRead(index, value, sizeof(value));
181  if (result == 0) {
182    for (i = 0; i < TPM_PCR_DIGEST; i++) {
183      printf("%02x", value[i]);
184    }
185    printf("\n");
186  }
187  return result;
188}
189
190static uint32_t HandlerRead(void) {
191  uint32_t index, size;
192  uint8_t value[4096];
193  uint32_t result;
194  int i;
195  if (nargs != 4) {
196    fprintf(stderr, "usage: tpmc read <index> <size>\n");
197    exit(OTHER_ERROR);
198  }
199  if (HexStringToUint32(args[2], &index) != 0 ||
200      HexStringToUint32(args[3], &size) != 0) {
201    fprintf(stderr, "<index> and <size> must be 32-bit hex (0x[0-9a-f]+)\n");
202    exit(OTHER_ERROR);
203  }
204  if (size > sizeof(value)) {
205    fprintf(stderr, "size of read (0x%x) is too big\n", size);
206    exit(OTHER_ERROR);
207  }
208  result = TlclRead(index, value, size);
209  if (result == 0 && size > 0) {
210    for (i = 0; i < size - 1; i++) {
211      printf("%x ", value[i]);
212    }
213    printf("%x\n", value[i]);
214  }
215  return result;
216}
217
218static uint32_t HandlerGetPermissions(void) {
219  uint32_t index, permissions, result;
220  if (nargs != 3) {
221    fprintf(stderr, "usage: tpmc getp <index>\n");
222    exit(OTHER_ERROR);
223  }
224  if (HexStringToUint32(args[2], &index) != 0) {
225    fprintf(stderr, "<index> must be 32-bit hex (0x[0-9a-f]+)\n");
226    exit(OTHER_ERROR);
227  }
228  result = TlclGetPermissions(index, &permissions);
229  if (result == 0) {
230    printf("space 0x%x has permissions 0x%x\n", index, permissions);
231  }
232  return result;
233}
234
235static uint32_t HandlerGetOwnership(void) {
236  uint8_t owned = 0;
237  uint32_t result;
238  if (nargs != 2) {
239    fprintf(stderr, "usage: tpmc getownership\n");
240    exit(OTHER_ERROR);
241  }
242  result = TlclGetOwnership(&owned);
243  if (result == 0) {
244    printf("Owned: %s\n", owned ? "yes" : "no");
245  }
246  return result;
247}
248
249static uint32_t HandlerGetRandom(void) {
250  uint32_t length, size;
251  uint8_t* bytes;
252  uint32_t result;
253  int i;
254  if (nargs != 3) {
255    fprintf(stderr, "usage: tpmc getrandom <size>\n");
256    exit(OTHER_ERROR);
257  }
258  if (HexStringToUint32(args[2], &length) != 0) {
259    fprintf(stderr, "<size> must be 32-bit hex (0x[0-9a-f]+)\n");
260    exit(OTHER_ERROR);
261  }
262  bytes = calloc(1, length);
263  if (bytes == NULL) {
264    perror("calloc");
265    exit(OTHER_ERROR);
266  }
267  result = TlclGetRandom(bytes, length, &size);
268  if (result == 0 && size > 0) {
269    for (i = 0; i < size; i++) {
270      printf("%02x", bytes[i]);
271    }
272    printf("\n");
273  }
274  free(bytes);
275  return result;
276}
277
278static uint32_t HandlerGetPermanentFlags(void) {
279  TPM_PERMANENT_FLAGS pflags;
280  uint32_t result = TlclGetPermanentFlags(&pflags);
281  if (result == 0) {
282#define P(name) printf("%s %d\n", #name, pflags.name)
283    P(disable);
284    P(ownership);
285    P(deactivated);
286    P(readPubek);
287    P(disableOwnerClear);
288    P(allowMaintenance);
289    P(physicalPresenceLifetimeLock);
290    P(physicalPresenceHWEnable);
291    P(physicalPresenceCMDEnable);
292    P(CEKPUsed);
293    P(TPMpost);
294    P(TPMpostLock);
295    P(FIPS);
296    P(Operator);
297    P(enableRevokeEK);
298    P(nvLocked);
299    P(readSRKPub);
300    P(tpmEstablished);
301    P(maintenanceDone);
302    P(disableFullDALogicInfo);
303#undef P
304  }
305  return result;
306}
307
308static uint32_t HandlerGetSTClearFlags(void) {
309  TPM_STCLEAR_FLAGS vflags;
310  uint32_t result = TlclGetSTClearFlags(&vflags);
311  if (result == 0) {
312#define P(name) printf("%s %d\n", #name, vflags.name)
313  P(deactivated);
314  P(disableForceClear);
315  P(physicalPresence);
316  P(physicalPresenceLock);
317  P(bGlobalLock);
318#undef P
319  }
320  return result;
321}
322
323
324static uint32_t HandlerSendRaw(void) {
325  uint8_t request[4096];
326  uint8_t response[4096];
327  uint32_t result;
328  int size;
329  int i;
330  if (nargs == 2) {
331    fprintf(stderr, "usage: tpmc sendraw <hex byte 0> ... <hex byte N>\n");
332    exit(OTHER_ERROR);
333  }
334  for (i = 0; i < nargs - 2 && i < sizeof(request); i++) {
335    if (HexStringToUint8(args[2 + i], &request[i]) != 0) {
336      fprintf(stderr, "bad byte value \"%s\"\n", args[2 + i]);
337      exit(OTHER_ERROR);
338    }
339  }
340  size = TlclPacketSize(request);
341  if (size != i) {
342    fprintf(stderr, "bad request: size field is %d, but packet has %d bytes\n",
343            size, i);
344    exit(OTHER_ERROR);
345  }
346  bzero(response, sizeof(response));
347  result = TlclSendReceive(request, response, sizeof(response));
348  if (result != 0) {
349    fprintf(stderr, "request failed with code %d\n", result);
350  }
351  size = TlclPacketSize(response);
352  if (size < 10 || size > sizeof(response)) {
353    fprintf(stderr, "unexpected response size %d\n", size);
354    exit(OTHER_ERROR);
355  }
356  for (i = 0; i < size; i++) {
357    printf("0x%02x ", response[i]);
358    if (i == size - 1 || (i + 1) % 8 == 0) {
359      printf("\n");
360    }
361  }
362  return result;
363}
364
365
366/* Table of TPM commands.
367 */
368command_record command_table[] = {
369  { "getflags", "getf", "read and print the value of selected flags",
370    HandlerGetFlags },
371  { "startup", "sta", "issue a Startup command", TlclStartup },
372  { "selftestfull", "test", "issue a SelfTestFull command", TlclSelfTestFull },
373  { "continueselftest", "ctest", "issue a ContinueSelfTest command",
374    TlclContinueSelfTest },
375  { "assertphysicalpresence", "ppon", "assert Physical Presence",
376    TlclAssertPhysicalPresence },
377  { "physicalpresencecmdenable", "ppcmd", "turn on software PP",
378    TlclPhysicalPresenceCMDEnable },
379  { "enable", "ena", "enable the TPM (needs PP)", TlclSetEnable },
380  { "disable", "dis", "disable the TPM (needs PP)", TlclClearEnable },
381  { "activate", "act", "activate the TPM (needs PP, maybe reboot)",
382    HandlerActivate },
383  { "deactivate", "deact", "deactivate the TPM (needs PP, maybe reboot)",
384    HandlerDeactivate },
385  { "clear", "clr", "clear the TPM owner (needs PP)", TlclForceClear },
386  { "setnvlocked", "setnv", "set the nvLocked flag permanently (IRREVERSIBLE!)",
387    TlclSetNvLocked },
388  { "lockphysicalpresence", "pplock", "lock (turn off) PP until reboot",
389    TlclLockPhysicalPresence },
390  { "setbgloballock", "block", "set the bGlobalLock until reboot",
391    TlclSetGlobalLock },
392  { "definespace", "def", "define a space (def <index> <size> <perm>)",
393    HandlerDefineSpace },
394  { "write", "write", "write to a space (write <index> [<byte0> <byte1> ...])",
395    HandlerWrite },
396  { "read", "read", "read from a space (read <index> <size>)",
397    HandlerRead },
398  { "pcrread", "pcr", "read from a PCR (pcrread <index>)",
399    HandlerPCRRead },
400  { "getownership", "geto", "print state of TPM ownership",
401    HandlerGetOwnership },
402  { "getpermissions", "getp", "print space permissions (getp <index>)",
403    HandlerGetPermissions },
404  { "getpermanentflags", "getpf", "print all permanent flags",
405    HandlerGetPermanentFlags },
406  { "getrandom", "rand", "read bytes from RNG (rand <size>)",
407    HandlerGetRandom },
408  { "getstclearflags", "getvf", "print all volatile (ST_CLEAR) flags",
409    HandlerGetSTClearFlags },
410  { "resume", "res", "execute TPM_Startup(ST_STATE)", TlclResume },
411  { "savestate", "save", "execute TPM_SaveState", TlclSaveState },
412  { "sendraw", "raw", "send a raw request and print raw response",
413    HandlerSendRaw },
414};
415
416static int n_commands = sizeof(command_table) / sizeof(command_table[0]);
417
418int main(int argc, char* argv[]) {
419  char *progname;
420  progname = strrchr(argv[0], '/');
421  if (progname)
422    progname++;
423  else
424    progname = argv[0];
425
426  if (argc < 2) {
427    fprintf(stderr, "usage: %s <TPM command> [args]\n   or: %s help\n",
428            progname, progname);
429    return OTHER_ERROR;
430  } else {
431    command_record* c;
432    const char* cmd = argv[1];
433    nargs = argc;
434    args = argv;
435
436    if (strcmp(cmd, "help") == 0) {
437      printf("%26s %7s  %s\n\n", "command", "abbr.", "description");
438      for (c = command_table; c < command_table + n_commands; c++) {
439        printf("%26s %7s  %s\n", c->name, c->abbr, c->description);
440      }
441      return 0;
442    }
443
444    TlclLibInit();
445
446    for (c = command_table; c < command_table + n_commands; c++) {
447      if (strcmp(cmd, c->name) == 0 || strcmp(cmd, c->abbr) == 0) {
448        return ErrorCheck(c->handler(), cmd);
449      }
450    }
451
452    /* No command matched. */
453    fprintf(stderr, "%s: unknown command: %s\n", progname, cmd);
454    return OTHER_ERROR;
455  }
456}
457