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