tpmc: define actions for all commands for tpm2
[vboot.git] / utility / tpmc.c
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
24 #ifdef TPM2_MODE
25 #define TPM_MODE_SELECT(_, tpm20_ver) tpm20_ver
26 #else
27 #define TPM_MODE_SELECT(tpm12_ver, _) tpm12_ver
28 #endif
29
30 #define TPM_MODE_STRING TPM_MODE_SELECT("1.2", "2.0")
31 #define TPM12_NEEDS_PP TPM_MODE_SELECT(" (needs PP)", "")
32 #define TPM12_NEEDS_PP_REBOOT TPM_MODE_SELECT(" (needs PP, maybe reboot)", "")
33
34 #define TPM20_NOT_IMPLEMENTED_DESCR(descr) \
35   descr TPM_MODE_SELECT("", " [not-implemented for TPM2.0]")
36 #define TPM20_NOT_IMPLEMENTED_HANDLER(handler) \
37   TPM_MODE_SELECT(handler, HandlerNotImplementedForTPM2)
38 #define TPM20_NOT_IMPLEMENTED(descr, handler) \
39   TPM20_NOT_IMPLEMENTED_DESCR(descr), \
40   TPM20_NOT_IMPLEMENTED_HANDLER(handler)
41
42 #define TPM20_DOES_NOTHING_DESCR(descr) \
43   descr TPM_MODE_SELECT("", " [no-op for TPM2.0]")
44 #define TPM20_DOES_NOTHING_HANDLER(handler) \
45   TPM_MODE_SELECT(handler, HandlerDoNothingForTPM2)
46 #define TPM20_DOES_NOTHING(descr, handler) \
47   TPM20_DOES_NOTHING_DESCR(descr), \
48   TPM20_DOES_NOTHING_HANDLER(handler)
49
50 typedef struct command_record {
51   const char* name;
52   const char* abbr;
53   const char* description;
54   uint32_t (*handler)(void);
55 } command_record;
56
57 /* Set in main, consumed by handler functions below.  We use global variables
58  * so we can also choose to call Tlcl*() functions directly; they don't take
59  * argv/argc.
60  */
61 int nargs;
62 char** args;
63
64 /* Converts a string in the form 0x[0-9a-f]+ to a 32-bit value.  Returns 0 for
65  * success, non-zero for failure.
66  */
67 int HexStringToUint32(const char* string, uint32_t* value) {
68   char tail[1];
69   /* strtoul is not as good because it overflows silently */
70   char* format = strncmp(string, "0x", 2) ? "%8x%s" : "0x%8x%s";
71   int n = sscanf(string, format, value, tail);
72   return n != 1;
73 }
74
75 /* Converts a string in the form [0-9a-f]+ to an 8-bit value.  Returns 0 for
76  * success, non-zero for failure.
77  */
78 int HexStringToUint8(const char* string, uint8_t* value) {
79   char* end;
80   uint32_t large_value = strtoul(string, &end, 16);
81   if (*end != '\0' || large_value > 0xff) {
82     return 1;
83   }
84   *value = large_value;
85   return 0;
86 }
87
88 int HexStringToArray(const char* string, uint8_t* value, int num_bytes) {
89   int len = strlen(string);
90   if (!strncmp(string, "0x", 2)) {
91     string += 2;
92     len -= 2;
93   }
94   if (len != num_bytes * 2) {
95     return 1;
96   }
97   for (; len > 0; string += 2, len -= 2, value++) {
98     if (sscanf(string, "%2hhx", value) != 1) {
99       return 1;
100     }
101   }
102   return 0;
103 }
104
105 /* TPM error check and reporting.  Returns 0 if |result| is 0 (TPM_SUCCESS).
106  * Otherwise looks up a TPM error in the error table and prints the error if
107  * found.  Then returns min(result, OTHER_ERROR) since some error codes, such
108  * as TPM_E_RETRY, do not fit in a byte.
109  */
110 uint8_t ErrorCheck(uint32_t result, const char* cmd) {
111   uint8_t exit_code = result > OTHER_ERROR ? OTHER_ERROR : result;
112   if (result == 0) {
113     return 0;
114   } else {
115     int i;
116     int n = sizeof(tpm_error_table) / sizeof(tpm_error_table[0]);
117     fprintf(stderr, "command \"%s\" failed with code 0x%x\n", cmd, result);
118     for (i = 0; i < n; i++) {
119       if (tpm_error_table[i].code == result) {
120         fprintf(stderr, "%s\n%s\n", tpm_error_table[i].name,
121                 tpm_error_table[i].description);
122         return exit_code;
123       }
124     }
125     fprintf(stderr, "the TPM error code is unknown to this program\n");
126     return exit_code;
127   }
128 }
129
130 /* Handler functions.  These wouldn't exist if C had closures.
131  */
132 /* TODO(apronin): stub for selected flags for TPM2 */
133 #ifdef TPM2_MODE
134 static uint32_t HandlerGetFlags(void) {
135   fprintf(stderr, "getflags not implemented for TPM2\n");
136   exit(OTHER_ERROR);
137 }
138 #else
139 static uint32_t HandlerGetFlags(void) {
140   uint8_t disabled;
141   uint8_t deactivated;
142   uint8_t nvlocked;
143   uint32_t result = TlclGetFlags(&disabled, &deactivated, &nvlocked);
144   if (result == 0) {
145     printf("disabled: %d\ndeactivated: %d\nnvlocked: %d\n",
146            disabled, deactivated, nvlocked);
147   }
148   return result;
149 }
150 #endif
151
152 #ifndef TPM2_MODE
153 static uint32_t HandlerActivate(void) {
154   return TlclSetDeactivated(0);
155 }
156
157 static uint32_t HandlerDeactivate(void) {
158   return TlclSetDeactivated(1);
159 }
160 #endif
161
162 static uint32_t HandlerDefineSpace(void) {
163   uint32_t index, size, perm;
164   if (nargs != 5) {
165     fprintf(stderr, "usage: tpmc def <index> <size> <perm>\n");
166     exit(OTHER_ERROR);
167   }
168   if (HexStringToUint32(args[2], &index) != 0 ||
169       HexStringToUint32(args[3], &size) != 0 ||
170       HexStringToUint32(args[4], &perm) != 0) {
171     fprintf(stderr, "<index>, <size>, and <perm> must be "
172             "32-bit hex (0x[0-9a-f]+)\n");
173     exit(OTHER_ERROR);
174   }
175   return TlclDefineSpace(index, perm, size);
176 }
177
178 static uint32_t HandlerWrite(void) {
179   uint32_t index, size;
180   uint8_t value[TPM_MAX_COMMAND_SIZE];
181   char** byteargs;
182   int i;
183   if (nargs < 3) {
184     fprintf(stderr, "usage: tpmc write <index> [<byte0> <byte1> ...]\n");
185     exit(OTHER_ERROR);
186   }
187   if (HexStringToUint32(args[2], &index) != 0) {
188     fprintf(stderr, "<index> must be 32-bit hex (0x[0-9a-f]+)\n");
189     exit(OTHER_ERROR);
190   }
191   size = nargs - 3;
192   if (size > sizeof(value)) {
193     fprintf(stderr, "byte array too large\n");
194     exit(OTHER_ERROR);
195   }
196
197   byteargs = args + 3;
198   for (i = 0; i < size; i++) {
199     if (HexStringToUint8(byteargs[i], &value[i]) != 0) {
200       fprintf(stderr, "invalid byte %s, should be [0-9a-f][0-9a-f]?\n",
201               byteargs[i]);
202       exit(OTHER_ERROR);
203     }
204   }
205
206   if (size == 0) {
207 #ifndef TPM2_MODE
208     if (index == TPM_NV_INDEX_LOCK) {
209       fprintf(stderr, "This would set the nvLocked bit. "
210               "Use \"tpmc setnv\" instead.\n");
211       exit(OTHER_ERROR);
212     }
213 #endif
214     printf("warning: zero-length write\n");
215   } else {
216     printf("writing %d byte%s\n", size, size > 1 ? "s" : "");
217   }
218
219   return TlclWrite(index, value, size);
220 }
221
222 static uint32_t HandlerPCRRead(void) {
223   uint32_t index;
224   uint8_t value[TPM_PCR_DIGEST];
225   uint32_t result;
226   int i;
227   if (nargs != 3) {
228     fprintf(stderr, "usage: tpmc pcrread <index>\n");
229     exit(OTHER_ERROR);
230   }
231   if (HexStringToUint32(args[2], &index) != 0) {
232     fprintf(stderr, "<index> must be 32-bit hex (0x[0-9a-f]+)\n");
233     exit(OTHER_ERROR);
234   }
235   result = TlclPCRRead(index, value, sizeof(value));
236   if (result == 0) {
237     for (i = 0; i < TPM_PCR_DIGEST; i++) {
238       printf("%02x", value[i]);
239     }
240     printf("\n");
241   }
242   return result;
243 }
244
245 static uint32_t HandlerPCRExtend(void) {
246   uint32_t index;
247   uint8_t value[TPM_PCR_DIGEST];
248   if (nargs != 4) {
249     fprintf(stderr, "usage: tpmc pcrextend <index> <extend_hash>\n");
250     exit(OTHER_ERROR);
251   }
252   if (HexStringToUint32(args[2], &index) != 0) {
253     fprintf(stderr, "<index> must be 32-bit hex (0x[0-9a-f]+)\n");
254     exit(OTHER_ERROR);
255   }
256   if (HexStringToArray(args[3], value, TPM_PCR_DIGEST)) {
257     fprintf(stderr, "<extend_hash> must be a 20-byte hex string\n");
258     exit(OTHER_ERROR);
259   }
260   return TlclExtend(index, value, value);
261 }
262
263 static uint32_t HandlerRead(void) {
264   uint32_t index, size;
265   uint8_t value[4096];
266   uint32_t result;
267   int i;
268   if (nargs != 4) {
269     fprintf(stderr, "usage: tpmc read <index> <size>\n");
270     exit(OTHER_ERROR);
271   }
272   if (HexStringToUint32(args[2], &index) != 0 ||
273       HexStringToUint32(args[3], &size) != 0) {
274     fprintf(stderr, "<index> and <size> must be 32-bit hex (0x[0-9a-f]+)\n");
275     exit(OTHER_ERROR);
276   }
277   if (size > sizeof(value)) {
278     fprintf(stderr, "size of read (0x%x) is too big\n", size);
279     exit(OTHER_ERROR);
280   }
281   result = TlclRead(index, value, size);
282   if (result == 0 && size > 0) {
283     for (i = 0; i < size - 1; i++) {
284       printf("%x ", value[i]);
285     }
286     printf("%x\n", value[i]);
287   }
288   return result;
289 }
290
291 static uint32_t HandlerGetPermissions(void) {
292   uint32_t index, permissions, result;
293   if (nargs != 3) {
294     fprintf(stderr, "usage: tpmc getp <index>\n");
295     exit(OTHER_ERROR);
296   }
297   if (HexStringToUint32(args[2], &index) != 0) {
298     fprintf(stderr, "<index> must be 32-bit hex (0x[0-9a-f]+)\n");
299     exit(OTHER_ERROR);
300   }
301   result = TlclGetPermissions(index, &permissions);
302   if (result == 0) {
303     printf("space 0x%x has permissions 0x%x\n", index, permissions);
304   }
305   return result;
306 }
307
308 static uint32_t HandlerGetOwnership(void) {
309   uint8_t owned = 0;
310   uint32_t result;
311   if (nargs != 2) {
312     fprintf(stderr, "usage: tpmc getownership\n");
313     exit(OTHER_ERROR);
314   }
315   result = TlclGetOwnership(&owned);
316   if (result == 0) {
317     printf("Owned: %s\n", owned ? "yes" : "no");
318   }
319   return result;
320 }
321
322 static uint32_t HandlerGetRandom(void) {
323   uint32_t length, size;
324   uint8_t* bytes;
325   uint32_t result;
326   int i;
327   if (nargs != 3) {
328     fprintf(stderr, "usage: tpmc getrandom <size>\n");
329     exit(OTHER_ERROR);
330   }
331   if (HexStringToUint32(args[2], &length) != 0) {
332     fprintf(stderr, "<size> must be 32-bit hex (0x[0-9a-f]+)\n");
333     exit(OTHER_ERROR);
334   }
335   bytes = calloc(1, length);
336   if (bytes == NULL) {
337     perror("calloc");
338     exit(OTHER_ERROR);
339   }
340   result = TlclGetRandom(bytes, length, &size);
341   if (result == 0 && size > 0) {
342     for (i = 0; i < size; i++) {
343       printf("%02x", bytes[i]);
344     }
345     printf("\n");
346   }
347   free(bytes);
348   return result;
349 }
350
351 static uint32_t HandlerGetPermanentFlags(void) {
352   TPM_PERMANENT_FLAGS pflags;
353   uint32_t result = TlclGetPermanentFlags(&pflags);
354   if (result == 0) {
355 #define P(name) printf("%s %d\n", #name, pflags.name)
356 #ifdef TPM2_MODE
357     P(ownerAuthSet);
358     P(endorsementAuthSet);
359     P(lockoutAuthSet);
360     P(disableClear);
361     P(inLockout);
362     P(tpmGeneratedEPS);
363 #else
364     P(disable);
365     P(ownership);
366     P(deactivated);
367     P(readPubek);
368     P(disableOwnerClear);
369     P(allowMaintenance);
370     P(physicalPresenceLifetimeLock);
371     P(physicalPresenceHWEnable);
372     P(physicalPresenceCMDEnable);
373     P(CEKPUsed);
374     P(TPMpost);
375     P(TPMpostLock);
376     P(FIPS);
377     P(Operator);
378     P(enableRevokeEK);
379     P(nvLocked);
380     P(readSRKPub);
381     P(tpmEstablished);
382     P(maintenanceDone);
383     P(disableFullDALogicInfo);
384 #endif
385 #undef P
386   }
387   return result;
388 }
389
390 static uint32_t HandlerGetSTClearFlags(void) {
391   TPM_STCLEAR_FLAGS vflags;
392   uint32_t result = TlclGetSTClearFlags(&vflags);
393   if (result == 0) {
394 #define P(name) printf("%s %d\n", #name, vflags.name)
395 #ifdef TPM2_MODE
396   P(phEnable);
397   P(shEnable);
398   P(ehEnable);
399   P(phEnableNV);
400   P(orderly);
401 #else
402   P(deactivated);
403   P(disableForceClear);
404   P(physicalPresence);
405   P(physicalPresenceLock);
406   P(bGlobalLock);
407 #endif
408 #undef P
409   }
410   return result;
411 }
412
413 static uint32_t HandlerSendRaw(void) {
414   uint8_t request[4096];
415   uint8_t response[4096];
416   uint32_t result;
417   int size;
418   int i;
419   if (nargs == 2) {
420     fprintf(stderr, "usage: tpmc sendraw <hex byte 0> ... <hex byte N>\n");
421     exit(OTHER_ERROR);
422   }
423   for (i = 0; i < nargs - 2 && i < sizeof(request); i++) {
424     if (HexStringToUint8(args[2 + i], &request[i]) != 0) {
425       fprintf(stderr, "bad byte value \"%s\"\n", args[2 + i]);
426       exit(OTHER_ERROR);
427     }
428   }
429   size = TlclPacketSize(request);
430   if (size != i) {
431     fprintf(stderr, "bad request: size field is %d, but packet has %d bytes\n",
432             size, i);
433     exit(OTHER_ERROR);
434   }
435   bzero(response, sizeof(response));
436   result = TlclSendReceive(request, response, sizeof(response));
437   if (result != 0) {
438     fprintf(stderr, "request failed with code %d\n", result);
439   }
440   size = TlclPacketSize(response);
441   if (size < 10 || size > sizeof(response)) {
442     fprintf(stderr, "unexpected response size %d\n", size);
443     exit(OTHER_ERROR);
444   }
445   for (i = 0; i < size; i++) {
446     printf("0x%02x ", response[i]);
447     if (i == size - 1 || (i + 1) % 8 == 0) {
448       printf("\n");
449     }
450   }
451   return result;
452 }
453
454 #ifdef TPM2_MODE
455 static uint32_t HandlerDoNothingForTPM2(void) {
456   return 0;
457 }
458
459 static uint32_t HandlerNotImplementedForTPM2(void) {
460   fprintf(stderr, "%s: not implemented for TPM2.0\n", args[1]);
461   exit(OTHER_ERROR);
462 }
463 #endif
464
465 /* Table of TPM commands.
466  */
467 command_record command_table[] = {
468   { "getflags", "getf", "read and print the value of selected flags",
469     HandlerGetFlags },
470   { "startup", "sta", "issue a Startup command", TlclStartup },
471   { "selftestfull", "test", "issue a SelfTestFull command", TlclSelfTestFull },
472   { "continueselftest", "ctest", "issue a ContinueSelfTest command",
473     TlclContinueSelfTest },
474   { "assertphysicalpresence", "ppon",
475     TPM20_DOES_NOTHING("assert Physical Presence",
476       TlclAssertPhysicalPresence) },
477   { "physicalpresencecmdenable", "ppcmd",
478     TPM20_NOT_IMPLEMENTED("turn on software PP",
479       TlclPhysicalPresenceCMDEnable) },
480   { "enable", "ena",
481     TPM20_DOES_NOTHING("enable the TPM" TPM12_NEEDS_PP,
482       TlclSetEnable) },
483   { "disable", "dis",
484     TPM20_NOT_IMPLEMENTED("disable the TPM" TPM12_NEEDS_PP,
485       TlclClearEnable) },
486   { "activate", "act",
487     TPM20_DOES_NOTHING("activate the TPM" TPM12_NEEDS_PP_REBOOT,
488       HandlerActivate) },
489   { "deactivate", "deact",
490     TPM20_NOT_IMPLEMENTED("deactivate the TPM" TPM12_NEEDS_PP_REBOOT,
491       HandlerDeactivate) },
492   { "clear", "clr",
493     "clear the TPM owner" TPM12_NEEDS_PP,
494     TlclForceClear },
495   { "setnvlocked", "setnv",
496     TPM20_NOT_IMPLEMENTED("set the nvLocked flag permanently (IRREVERSIBLE!)",
497       TlclSetNvLocked) },
498   { "lockphysicalpresence", "pplock",
499     TPM_MODE_SELECT("lock (turn off) PP until reboot",
500       "set rollback protection lock for kernel image until reboot"),
501     TlclLockPhysicalPresence },
502   { "setbgloballock", "block",
503     TPM_MODE_SELECT("set the bGlobalLock until reboot",
504       "set rollback protection lock for R/W firmware until reboot"),
505     TlclSetGlobalLock },
506   { "definespace", "def", "define a space (def <index> <size> <perm>)",
507     HandlerDefineSpace },
508   { "write", "write", "write to a space (write <index> [<byte0> <byte1> ...])",
509     HandlerWrite },
510   { "read", "read", "read from a space (read <index> <size>)",
511     HandlerRead },
512   { "pcrread", "pcr", "read from a PCR (pcrread <index>)",
513     HandlerPCRRead },
514   { "pcrextend", "extend", "extend a PCR (extend <index> <extend_hash>)",
515     HandlerPCRExtend },
516   { "getownership", "geto", "print state of TPM ownership",
517     HandlerGetOwnership },
518   { "getpermissions", "getp", "print space permissions (getp <index>)",
519     HandlerGetPermissions },
520   { "getpermanentflags", "getpf", "print all permanent flags",
521     HandlerGetPermanentFlags },
522   { "getrandom", "rand", "read bytes from RNG (rand <size>)",
523     HandlerGetRandom },
524   { "getstclearflags", "getvf", "print all volatile (ST_CLEAR) flags",
525     HandlerGetSTClearFlags },
526   { "resume", "res", "execute TPM_Startup(ST_STATE)", TlclResume },
527   { "savestate", "save", "execute TPM_SaveState", TlclSaveState },
528   { "sendraw", "raw", "send a raw request and print raw response",
529     HandlerSendRaw },
530 };
531
532 static int n_commands = sizeof(command_table) / sizeof(command_table[0]);
533
534 int main(int argc, char* argv[]) {
535   char *progname;
536   uint32_t result;
537
538   progname = strrchr(argv[0], '/');
539   if (progname)
540     progname++;
541   else
542     progname = argv[0];
543
544   if (argc < 2) {
545     fprintf(stderr, "usage: %s <TPM command> [args]\n   or: %s help\n",
546             progname, progname);
547     return OTHER_ERROR;
548   } else {
549     command_record* c;
550     const char* cmd = argv[1];
551     nargs = argc;
552     args = argv;
553
554     if (strcmp(cmd, "help") == 0) {
555       printf("tpmc mode: TPM%s\n", TPM_MODE_STRING);
556       printf("%26s %7s  %s\n\n", "command", "abbr.", "description");
557       for (c = command_table; c < command_table + n_commands; c++) {
558         printf("%26s %7s  %s\n", c->name, c->abbr, c->description);
559       }
560       return 0;
561     }
562
563     result = TlclLibInit();
564     if (result) {
565       fprintf(stderr, "initialization failed with code %d\n", result);
566       return result > OTHER_ERROR ? OTHER_ERROR : result;
567     }
568
569     for (c = command_table; c < command_table + n_commands; c++) {
570       if (strcmp(cmd, c->name) == 0 || strcmp(cmd, c->abbr) == 0) {
571         return ErrorCheck(c->handler(), cmd);
572       }
573     }
574
575     /* No command matched. */
576     fprintf(stderr, "%s: unknown command: %s\n", progname, cmd);
577     return OTHER_ERROR;
578   }
579 }