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  */
HexStringToUint32(const char * string,uint32_t * value)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  */
HexStringToUint8(const char * string,uint8_t * value)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 /* 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  */
ErrorCheck(uint32_t result,const char * cmd)67 uint8_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  */
HandlerGetFlags(void)89 static 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 
HandlerActivate(void)101 static uint32_t HandlerActivate(void) {
102   return TlclSetDeactivated(0);
103 }
104 
HandlerDeactivate(void)105 static uint32_t HandlerDeactivate(void) {
106   return TlclSetDeactivated(1);
107 }
108 
HandlerDefineSpace(void)109 static 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 
HandlerWrite(void)125 static 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 
HandlerPCRRead(void)167 static 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 
HandlerRead(void)190 static 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 
HandlerGetPermissions(void)218 static 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 
HandlerGetOwnership(void)235 static 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 
HandlerGetRandom(void)249 static 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 
HandlerGetPermanentFlags(void)278 static 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 
HandlerGetSTClearFlags(void)308 static 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 
HandlerSendRaw(void)324 static 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  */
368 command_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 
416 static int n_commands = sizeof(command_table) / sizeof(command_table[0]);
417 
main(int argc,char * argv[])418 int 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